使用 Linq to Entities 查询创建 null ienumerable
Create null enumerable within Linq to Entities query
我正在开发一个 ASP.NET MVC 项目,该项目使用 Entity Framework。
我需要将从数据库中提取的值投影到 PropertyValue
类型中,如下所示:
public class PropertyValue {
public string StringValue { get; set; }
public bool? BoolValue { get; set; }
public int? IntValue { get; set; }
}
大多数时候,这很容易。要使用 "First Name" = "John" 和 "Is Archived" = true 过滤所有用户,我可以这样做:
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,并且我必须以相同的顺序设置它们。如果我不这样做,我会收到来自 Entity Framework 的错误消息,指出 PropertyValue 存在于查询中两个结构上不兼容的初始化中。
System.NotSupportedException: The type 'UMS.Utilities.PropertyValue' appears in two structurally incompatible initializations within a single LINQ to Entities query. A type can be initialized in two places in the same query, but only if the same properties are set in both places and those properties are set in the same order.
这不是问题,我只需确保所有属性都明确设置为 null。当我必须向我的 PropertyValue
类型添加另一个 属性 以便能够检索 ID 列表时,问题就出现了(在我的例子中,为用户检索所有选定的角色,即 "Admin", "Guardian", "Teacher", "Student")
我修改了 PropertyValue
class 以便能够存储 Guid 列表:
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: Unable to create a null constant value of type 'System.Collections.Generic.IEnumerable`1[[System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'. Only entity types, enumeration types or primitive types are supported in this context.
所以我无法在 EF 中创建可枚举的 null。但是我不能只忽略 属性 的初始化,否则我会得到结构不兼容的错误。我试过 GuidValues = Enumerable.Empty<Guid>()
,但我也不能在 EF 上下文中这样做。
如何解决此问题并在 EF 投影中创建 null 或空可枚举?
我不太确定用例。但总的来说,我同意这是令人讨厌的 EF 限制,尤其是 结构上不兼容的初始化 要求背后的原因,这迫使我们使用我将向您推荐的丑陋解决方法。
很快,除了 不包括 赋值到投影中之外,没有其他方法可以解决 null enumerable 赋值。接下来是尝试解决 结构上不兼容的初始化 问题。它在大多数情况下都有效,但 Concat
/ Union
场景除外。
首先定义一对子classes:
public class StringPropertyValue : PropertyValue { }
public class BoolPropertyValue : PropertyValue { }
public class IntPropertyValue : PropertyValue { }
public class GuidsPropertyValue : PropertyValue { }
名称并不重要,因为对这些 class 没有特殊限制,唯一重要的是它们 不同 。我们将使用那些 classes 代替 PropertyValue
.
进行投影
然后我们需要另一个简单的 class 像这样:
public class Property
{
public PropertyValue Value { get; set; }
}
需要解决另一个 EF 限制 - 不支持的 casts。为了投影 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);
我正在开发一个 ASP.NET MVC 项目,该项目使用 Entity Framework。
我需要将从数据库中提取的值投影到 PropertyValue
类型中,如下所示:
public class PropertyValue {
public string StringValue { get; set; }
public bool? BoolValue { get; set; }
public int? IntValue { get; set; }
}
大多数时候,这很容易。要使用 "First Name" = "John" 和 "Is Archived" = true 过滤所有用户,我可以这样做:
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,并且我必须以相同的顺序设置它们。如果我不这样做,我会收到来自 Entity Framework 的错误消息,指出 PropertyValue 存在于查询中两个结构上不兼容的初始化中。
System.NotSupportedException: The type 'UMS.Utilities.PropertyValue' appears in two structurally incompatible initializations within a single LINQ to Entities query. A type can be initialized in two places in the same query, but only if the same properties are set in both places and those properties are set in the same order.
这不是问题,我只需确保所有属性都明确设置为 null。当我必须向我的 PropertyValue
类型添加另一个 属性 以便能够检索 ID 列表时,问题就出现了(在我的例子中,为用户检索所有选定的角色,即 "Admin", "Guardian", "Teacher", "Student")
我修改了 PropertyValue
class 以便能够存储 Guid 列表:
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: Unable to create a null constant value of type 'System.Collections.Generic.IEnumerable`1[[System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'. Only entity types, enumeration types or primitive types are supported in this context.
所以我无法在 EF 中创建可枚举的 null。但是我不能只忽略 属性 的初始化,否则我会得到结构不兼容的错误。我试过 GuidValues = Enumerable.Empty<Guid>()
,但我也不能在 EF 上下文中这样做。
如何解决此问题并在 EF 投影中创建 null 或空可枚举?
我不太确定用例。但总的来说,我同意这是令人讨厌的 EF 限制,尤其是 结构上不兼容的初始化 要求背后的原因,这迫使我们使用我将向您推荐的丑陋解决方法。
很快,除了 不包括 赋值到投影中之外,没有其他方法可以解决 null enumerable 赋值。接下来是尝试解决 结构上不兼容的初始化 问题。它在大多数情况下都有效,但 Concat
/ Union
场景除外。
首先定义一对子classes:
public class StringPropertyValue : PropertyValue { }
public class BoolPropertyValue : PropertyValue { }
public class IntPropertyValue : PropertyValue { }
public class GuidsPropertyValue : PropertyValue { }
名称并不重要,因为对这些 class 没有特殊限制,唯一重要的是它们 不同 。我们将使用那些 classes 代替 PropertyValue
.
然后我们需要另一个简单的 class 像这样:
public class Property
{
public PropertyValue Value { get; set; }
}
需要解决另一个 EF 限制 - 不支持的 casts。为了投影 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);