【问题标题】:Linq to SQL Concat / Union does not working on the Custom ClassLinq to SQL Concat / Union 不适用于自定义类
【发布时间】:2017-01-12 10:26:58
【问题描述】:

Linq to SQL Concat / Union 不适用于自定义类。

自定义类

public class CustomClass
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public List<Accesses> Accesses { get; set; }
}

联合:

IQueryable<CustomClass> objQuery = context.Users       
    .Where(W => W.Auth == true)
    .Select(S => new CustomClass()
    {
        FirstName = S.FirstName,
        LastName = S.LastName,
        Accesses = context.AuthFalse
                    .Where(W => W.UID = S.ID)
                    .Select(S1 => new Accesses()
                    {
                        AccessesID = S1.AccessesID,                                                
                    })
                    .ToList(),
    })
    .Union(context.Users    
        .Where(W => W.Auth == true)                            
        .Select(S => new CustomClass()
        {
            FirstName = S.FirstName,
            LastName = S.LastName,
            Accesses = context.AuthTrue
                        .Where(W => W.UID = S.ID)
                        .Select(S1 => new Accesses()
                        {
                            AccessesID = S1.AccessesID,                                                
                        })
                        .ToList(),
        })
    );

当我执行上述查询时,出现以下错误:

“Distinct”操作无法应用于集合 指定参数的 ResultType。\r\n参数名称:参数

连接

IQueryable<CustomClass> objQuery = context.Users       
    .Where(W => W.Auth == true)
    .Select(S => new CustomClass()
    {
        FirstName = S.FirstName,
        LastName = S.LastName,
        Accesses = context.AuthFalse
                    .Where(W => W.UID = S.ID)
                    .Select(S1 => new Accesses()
                    {
                        AccessesID = S1.AccessesID,                                                
                    })
                    .ToList(),
    })
    .Concat(context.Users    
        .Where(W => W.Auth == true)                            
        .Select(S => new CustomClass()
        {
            FirstName = S.FirstName,
            LastName = S.LastName,
            Accesses = context.AuthTrue
                        .Where(W => W.UID = S.ID)
                        .Select(S1 => new Accesses()
                        {
                            AccessesID = S1.AccessesID,                                                
                        })
                        .ToList(),
        })
    );

当我执行上述查询时,出现以下错误:

{"不支持嵌套查询。Operation1='UnionAll' Operation2='MultiStreamNest'"}

最后,我想要做的是,如果用户表的 Auth 字段为 true,那么我想要来自 AuthTrue 的数据> 表并希望存储在 List 字段中,如果 Users 表的 Auth 字段为 false 比我想要的数据来自 AuthFalse 表并希望存储在 List 字段中。

我希望所有这些都在单个查询或最多 2 到 3 个查询中完成。

谢谢。

【问题讨论】:

  • 可能你应该重写你的 Concat 尝试如下:从用户开始,然后对 AuthFalse 和 AuthTrue 进行 2 次左连接,通过 user.Auth 字段和正确的右匹配添加过滤,使用用户选择匿名类。 Id、user.LastName、user.FirstName + AuthFalse 匹配或 AuthTrue 匹配。在这个阶段,必须通过强制转换 ToEnumerable 来查询,然后剩下的就是正确的分组 + 选择。
  • @VitaliyKalinin,谢谢,您能提供一些代码 sn-p 吗?因为我不明白你想说什么来实现。

标签: c# .net linq linq-to-sql linq-to-entities


【解决方案1】:

你应该像这样重写你的 Concat 尝试: 1.从用户开始 2. 然后对 AuthFalse 和 AuthTrue 做 2 个左连接 3.增加user.Auth字段过滤和正确匹配 4. 使用 user.Id、user.LastName、user.FirstName + AuthFalse 匹配或 AuthTrue 匹配选择匿名类。在这个阶段 queriable 必须通过强制转换 ToEnumerable 5.剩下的就是适当的分组+选择。

这是完整单元测试的步骤:

public class CustomClass
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public List<CustomAccesses> Accesses { get; set; }
    }

    public class CustomAccesses
    {
        public int Rights { get; set; }
    }


    public class Accesses
    {
        public Guid ID { get; set; }
        public Guid UID { get; set; }
        public int Rights { get; set; }
    }

    public class User
    {
        public Guid ID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public bool Auth { get; set; }
    }

    [TestFixture]
    public class QueryTests
    {
        private static readonly User TrueUser = new User
        {
            ID = Guid.NewGuid(),
            FirstName = "TrueFirstName",
            LastName = "TrueLastName",
            Auth = true
        };

        private static readonly User FalseUser = new User
        {
            ID = Guid.NewGuid(),
            FirstName = "FalseFirstName",
            LastName = "FalseLastName",
            Auth = false
        };

        private static readonly User[] DbUsersMock =
        {
            TrueUser,
            FalseUser
        };

        private static readonly Accesses[] DbAuthTrueMock =
        {
            new Accesses
            {
                ID = Guid.NewGuid(),
                UID = TrueUser.ID,
                Rights = 1
            },
            new Accesses
            {
                ID = Guid.NewGuid(),
                UID = TrueUser.ID,
                Rights = 2
            }
        };

        private static readonly Accesses[] DbAuthFalseMock =
        {
            new Accesses
            {
                ID = Guid.NewGuid(),
                UID = FalseUser.ID,
                Rights = -1
            },
            new Accesses
            {
                ID = Guid.NewGuid(),
                UID = FalseUser.ID,
                Rights = -2
            }
        };

        [Test]
        public void Test()
        {
            var users = DbUsersMock.AsQueryable();

            var authTrue = DbAuthTrueMock.AsQueryable();

            var authFalse = DbAuthFalseMock.AsQueryable();


            var result = users
                .GroupJoin(
                    authTrue,
                    u => u.ID,
                    ta => ta.UID,
                    (user, accesses) => new
                    {
                        user,
                        accesses = accesses.DefaultIfEmpty()
                    }
                )
                .SelectMany(
                    ua => ua
                        .accesses
                        .Select(
                            trueAccess => new
                            {
                                ua.user,
                                trueAccess
                            }
                        )
                )
                .GroupJoin(
                    authFalse,
                    ua => ua.user.ID,
                    fa => fa.UID,
                    (userAccess, accesses) => new
                    {
                        userAccess,
                        accesses = accesses.DefaultIfEmpty()
                    }
                )
                .SelectMany(
                    uaa => uaa
                        .accesses
                        .Select(
                            falseAccess => new
                            {
                                uaa.userAccess.user,
                                uaa.userAccess.trueAccess,
                                falseAccess
                            }
                        )
                )
                .Where(
                    uaa => uaa.user.Auth
                        ? uaa.trueAccess != null
                        : uaa.falseAccess != null
                )
                .Select(
                    uaa => new
                    {
                        uaa.user.ID,
                        uaa.user.FirstName,
                        uaa.user.LastName,
                        AccessID = uaa.user.Auth
                            ? uaa.trueAccess.ID
                            : uaa.falseAccess.ID,
                        Rights = uaa.user.Auth
                            ? uaa.trueAccess.Rights
                            : uaa.falseAccess.Rights
                    }
                )
                .OrderBy(uaa => uaa.ID)
                .AsEnumerable()
                .GroupBy(uaa => uaa.ID)
                .Select(
                    g => new CustomClass
                    {
                        FirstName = g.First().FirstName,
                        LastName = g.First().LastName,
                        Accesses = g
                            .GroupBy(uaa => uaa.AccessID)
                            .Select(
                                uaa => new CustomAccesses
                                {
                                    Rights = uaa.First().Rights
                                }
                            )
                            .ToList()
                    }
                )
                .ToArray();

            Assert.That(result.Length, Is.EqualTo(DbUsersMock.Length));
        }
    }

【讨论】:

  • 谢谢,您使用了AsEnumerable() 方法,该方法将触发查询,但我不想触发查询,因为之后我必须添加搜索关键字条件和排序功能它。所有这些我都想在数据库上而不是在内存中完成。
  • 我已将 AsEnumerable() 准确地放置在 LinqToSql 能够正确处理您的要求的位置。如果您的描述不完整,那么您应该准备好不完整的解决方案。
  • 实际上这段代码是 sn-p 用于解释,我已将其作为评论发布。我将在答案中复制粘贴我的评论。
【解决方案2】:

您可以通过多种方式做到这一点。一个简单的方法是: 取两个列表,一个用于当它为 TRUE 时,另一个用于当它为 FALSE 时。然后在结果列表中使用 ADDRANGE 方法。 此方法 (ADDRANGE) 仅在您使用列表时有效。

例子:

var listTrue = ctx.Users.Where(...).Select(s=> new CustomClass{...}).ToList();
var listFalse = ctx.Users.Where(...).Select(s=> new CustomClass{...}).ToList();
var result = new List<CustomClass>();
result.AddRange(listTrue);
result.AddRange(listFalse);
return result;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-10
    • 1970-01-01
    • 1970-01-01
    • 2021-11-08
    • 1970-01-01
    相关资源
    最近更新 更多