我正在使用一个使用Entity Framework的ASP.NET MVC项目。
我需要将从数据库中提取的值投影到PropertyValue
类型,如下所示:
public class PropertyValue {
public string StringValue { get; set; }
public bool? BoolValue { get; set; }
public int? IntValue { get; set; }
}
大多数时候,这很容易。使用" First Name"过滤所有用户="约翰"和#34;存档" =真,我能做到:
usersQuery
.Where(u =>
new PropertyValue {
StringValue = u.FirstName,
BoolValue = null,
IntValue = null
}.StringValue == "John")
.Where(u =>
new PropertyValue {
StringValue = null,
BoolValue = u.IsArchived,
IntValue = null
}.BoolValue == true);
显然这是一个荒谬的查询,但我根据用户输入逐个构建这些查询。这些查询需要是可组合的,这就是为什么我必须将PropertyValue
的所有未使用属性显式设置为null,并且我必须以相同的顺序设置它们。如果我不这样做,我将从实体框架中收到错误,指出PropertyValue存在于查询中的两个结构不兼容的初始化中。
System.NotSupportedException:类型' UMS.Utilities.PropertyValue'在单个LINQ to Entities查询中出现两个结构不兼容的初始化。可以在同一查询中的两个位置初始化类型,但前提是在两个位置都设置了相同的属性,并且这些属性的设置顺序相同。
这不是问题,我可以确保将所有属性显式设置为null。当我必须向我的PropertyValue
类型添加另一个属性时,问题就出现了,以便能够检索ID列表(在我的情况下,检索用户的所有选定角色,即" Admin" ," Guardian","老师","学生")
我修改了我的PropertyValue
课程,以便能够存储Guids列表:
public class PropertyValue {
public string StringValue { get; set; }
public bool? BoolValue { get; set; }
public int? IntValue { get; set; }
public IEnumerable<Guid> GuidValues { get; set; }
}
我现在可以查询用户角色,如下所示:
usersQuery
.Select(u => new PropertyValue {
StringValue = null,
BoolValue = null,
IntValue = null,
GuidValues = u.UserRoles.Select(ur => ur.Role_ID)
});
这很有效。拉名字现在看起来像这样:
usersQuery
.Select(u => new PropertyValue {
StringValue = u.FirstName,
BoolValue = null,
IntValue = null,
GuidValues = null
});
但是我收到以下错误:
System.NotSupportedException:无法创建类型为&#39; System.Collections.Generic.IEnumerable`1 [[System.Guid,mscorlib,Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089]的null常量值]&#39 ;.在此上下文中仅支持实体类型,枚举类型或基元类型。
所以我无法在EF中创建null枚举。但我不能忽略属性的初始化,否则我会得到结构上不兼容的错误。我尝试过做GuidValues = Enumerable.Empty<Guid>()
,但我也无法在EF环境中做到这一点。
如何解决此问题并在EF投影中创建null或空枚举?
答案 0 :(得分:3)
我对用例不太确定。但总的来说,我同意它令人讨厌的EF限制,特别是结构上不兼容的初始化要求背后的推理,这迫使我们使用一个丑陋的解决方法,我会建议你。
很快,除不包括投影分配外,无法解决 null可枚举分配。接下来是尝试解决结构不兼容的初始化问题。除Concat
/ Union
场景外,它适用于大多数情况。
首先定义几个子类:
public class StringPropertyValue : PropertyValue { }
public class BoolPropertyValue : PropertyValue { }
public class IntPropertyValue : PropertyValue { }
public class GuidsPropertyValue : PropertyValue { }
这些名称并不重要,因为这些类没有特殊限制,唯一重要的是它们不同。我们将这些类用于投影,而不是PropertyValue
。
然后我们需要另一个这样的简单类:
public class Property
{
public PropertyValue Value { get; set; }
}
需要解决另一个EF限制 - 不受支持的强制转换。为了投射PropertyValue
而不是不受支持的
(PropertyValue)new StringPropertyValue { StringValue = .. }
我们将使用丑陋但受支持的
执行演员表new Property { Value = new StringPropertyValue { StringValue = ... } }.Value
就是这样。
以下是您的示例的等效工作版本:
(1)
usersQuery
.Where(u => new Property { Value = new StringPropertyValue {
StringValue = u.FirstName
}}.Value.StringValue == "John")
.Where(u => new Property { Value = new BoolPropertyValue {
BoolValue = u.IsArchived
}}.Value.BoolValue == true);
(2)
usersQuery
.Select(u => new Property { Value = new GuidsPropertyValue {
GuidValues = u.UserRoles.Select(ur => ur.Role_ID)
}}.Value);
(3)
usersQuery
.Select(u => new Property { Value = new StringPropertyValue {
StringValue = u.FirstName
}}.Value);