在Linq to Entities查询中创建null可枚举

时间:2016-05-09 00:04:52

标签: c# asp.net-mvc entity-framework linq linq-to-entities

我正在使用一个使用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或空枚举?

1 个答案:

答案 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);