IQueryable Union,无法创建类型为'System.Collections.Generic.IEnumerable`1 [[System.Int32]的null常量值

时间:2016-07-12 14:50:17

标签: c# .net sql-server entity-framework linq

我正在尝试使用union实现对Entity Framework的LINQ查询。假设我有2个表:用户和TempUsers。我试着:

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消息:

  

无法创建类型的null常量值   'System.Collections.Generic.IEnumerable`1 [[System.Int32,mscorlib,   Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089]]'。   仅支持实体类型,枚举类型或基元类型   在这种情况下。

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

  

“UserModel”类型显示为两个   单个LINQ中的结构不兼容的初始化   实体查询。一个类型可以在同一个地方初始化   查询,但仅当在两个地方都设置了相同的属性时   这些属性的设置顺序相同。

如何修复它的想法?谢谢!

2 个答案:

答案 0 :(得分:2)

为了避免这些异常,你可以在内存中调用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; }
}

然后尝试在服务器端执行union,您还需要调用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>

,此解决方案的缺点

答案 1 :(得分:0)

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

由于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();

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