IQueryable Union,无法创建类型为"System.Collections.Generic.IEnumerable"1[[System.Int32



我正在尝试使用联合实现对实体框架的 LINQ 查询。假设我有 2 个表:用户和临时用户。我尝试:

var users = context.Users
        .Select(u => new UserModel 
        {
            Name = u.Name,
            Roles = u.Roles.Select(r => r.Id)
        })
    .Union(context.TempUsers
        .Select(u => new UserModel 
        {
            Name = u.Name,
            Roles = null
        }))
.ToList();

型号类别:

public class UserModel
{
    public string Name { get; set; }
    public IEnumerable<int> Roles { get; set; }
}

我收到消息NotSupportedException

无法创建类型的空常量值 'System.Collections.Generic.IEnumerable'1[[System.Int32, mscorlib, 版本=4.0.0.0,区域性=中性,公钥令牌=b77a5c561934e089]]'。 仅支持实体类型、枚举类型或基元类型 在这种情况下。

如果我删除Roles = null,我会收到NotSupportedException消息:

类型"用户模型"出现在两个 单个 LINQ to 中结构不兼容的初始化 实体查询。一个类型可以在同一位置的两个位置初始化 查询,但前提是在两个位置和 这些属性按相同的顺序设置。

一个想法如何解决它?谢谢!

若要避免这些异常,可以在内存中调用AsEnumerable扩展方法进行联合:

var users = context.Users
        .Select(u => new UserModel 
        {
            Name = u.Name,
            Roles = u.Roles.Select(r => r.Id)
        })
        .AsEnumerable() //Add this 
        .Union(context.TempUsers
        .Select(u => new UserModel 
        {
            Name = u.Name,
        }))
.ToList();

问题是您正在尝试使用具有复杂类型作为属性的 DTO 进行投影。这不允许你分配默认值,因为当你尝试投影查询时,EF 仅支持实体类型、枚举类型或基元类型。也不值得使用匿名类型,因为两个投影都必须基于具有相同属性名称的匿名类型,因此,最后是相同的问题。

更新

现在我看到了使用继承的解决方案,我想到了这个想法。

public TempUser
{
  public string Name { get; set; }
} 
public UserModel:TempUser
{
  public IEnumerable<int> Roles { get; set; }
}

然后,要尝试在服务器端执行联合,您还需要调用OfType扩展方法

var query=context.TempUsers
          .Select(u => new TempUser
          {
            Name = u.Name,
          })
          .Union(context.Users
          .Select(u => new UserModel 
          {
            Name = u.Name,
            Roles = u.Roles.Select(r => r.Id)
          }).OfType<TempUser>())
          .ToList();

如果结果将是List<TempUser>,则此解决方案的缺点

第一个例外是无法避免的,但第二个例外可以通过以下技巧。

由于 EF 抱怨以不同的方式初始化同一类型,因此可以创建和初始化从原始类型派生的不同类型

例如:

public class TempUserModel : UserModel { }

然后

var users = context.Users
        .Select(u => new UserModel 
        {
            Name = u.Name,
            Roles = u.Roles.Select(r => r.Id)
        })
    .Union(context.TempUsers
        .Select(u => new TempUserModel 
        {
            Name = u.Name
        }))
    .ToList();

更新:上述技巧适用于单值属性,但不适用于集合类型属性。更重要的是,事实证明根本不支持带有嵌套子查询的Concat/Union。因此,在SQL中执行此操作的唯一方法是首先执行Concat(顺便说一句,相当于SQL UNION ALL)主表,然后进行条件连接,但我不确定这是否值得付出努力。最好使用当前接受的答案中的一些混合方法。

相关内容

  • 没有找到相关文章

最新更新