我正在尝试使用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中的结构不兼容的初始化 实体查询。一个类型可以在同一个地方初始化 查询,但仅当在两个地方都设置了相同的属性时 这些属性的设置顺序相同。
如何修复它的想法?谢谢!
答案 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
)主表,然后执行条件连接,但我不是确定它值得付出努力。更好地使用当前接受的答案中的一些混合方法。