将只读属性分配给实体框架中的新对象

时间:2015-02-17 18:03:35

标签: c# linq-to-entities entity-framework-6

我有一个映射到Entity Framework数据库的对象。这是一个相当大的对象,在获取它们的列表时,我只想要一个数据的子集。

问题是我有两个我需要的只读属性,它们在TypeId字段周围有一些重要的逻辑。这是一个例子:

这是我的类映射到实体

public class MyBigObject
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int OwnerId { get; set; }
    public string TypeId { get; set; }
    public bool IsFoo 
    { 
        get { /* complicated logic here for checking if TypeId IsFoo */ }
    }
    public bool IsBar 
    { 
        get { /* complicated logic here for checking if TypeId IsBar */ }
    }

    /* snip about 30 other columns */
}

这一切都很好,花花公子,非常适合拉这个对象的列表。但是,随着时间的推移,对象变得很大,我想减少Entity检索的数据量。出于这个原因,我们创建了一个未映射到Entity的新DTO类。

public class MyBigObjectItem
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string TypeId { get; set; }
    public bool IsFoo { get; set; }
    public bool IsBar { get; set; }
    public bool HasObject1 { get; set; }
    public bool HasObject2 { get; set; }
}

它只是一个非常简单的吸气剂和制定者容器。

然后,对于我的linq查询,我有类似的东西

public Task<List<MyBigObjectItem>> GetMyBigObjectItemsAsync(int ownerId, CancellationToken cancellationToken = default(CancellationToken))
{
    return (
        from obj in DataContext.MyBigObject
        join object1 in DataContext.SomeObject1 on obj.Id equals object1.ObjectId into object1items
        join object2 in DataContext.SomeObject2 on obj.Id equals object2.ObjectId into object2items
        where obj.OwnerId = ownerId
        orderby obj.Name
        select new MyBigObjectItem
        {
            Id = obj.Id,
            Name = obj.Name,
            TypeId = obj.TypeId,
            IsFoo = obj.IsFoo,
            IsBar = obj.IsBar,
            HasObject1 = object1items.Any(),
            HasObject2 = object2items.Any()
        }
    ).ToListAsync(cancellationToken);
}

在那个查询中有很多事情要发生,但我想给出一个我正在做的事情的准确例子。我们正在加入另外两个我们有一对多关系的对象。我只需要知道是否存在任何关系。

问题出现在IsFoo = obj.IsFoo,IsBar = obj.IsBar,行,其中Intellitrace显示错误:

  

指定的类型成员&#39; IsFoo&#39; LINQ to Entities不支持。仅支持初始化程序,实体成员和实体导航属性。&#34; (System.NotSupportedException)

关于如何使这项工作的任何想法,或者我注定要获得完整对象的列表然后将它们转换为我的DTO?

2 个答案:

答案 0 :(得分:1)

  

...或者我注定要获取完整对象的列表,然后将它们转换为我的DTO?

差不多。在评估查询之前完成的任何事情都是实体框架可以转换为SQL的东西,它排除了任何自定义属性,例如您创建的属性。您唯一真正的替代方法是创建一个存储过程来执行这些属性中包含的逻辑,只返回IsFooIsBar的结果布尔值,然后使用该SP来检索您的对象。根据此查询的复杂程度,无论如何,这可能是一个很好的举动。

答案 1 :(得分:0)

您可以使用精彩的DelegateDecompiler库将复杂属性反编译为其实现,然后可以将其转换为SQL代码。你只需用NuGet安装它,然后用它装饰你的属性:

public class MyBigObject
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int OwnerId { get; set; }
    public string TypeId { get; set; }
    [Computed]
    public bool IsFoo 
    { 
        get { /* complicated logic here for checking if TypeId IsFoo */ }
    }
    [Computed]
    public bool IsBar 
    { 
        get { /* complicated logic here for checking if TypeId IsBar */ }
    }

    /* snip about 30 other columns */
}

然后在IQueryable Query上调用Decompile()

public Task<List<MyBigObjectItem>> GetMyBigObjectItemsAsync(int ownerId, CancellationToken cancellationToken = default(CancellationToken))
{
    return (
        from obj in DataContext.MyBigObject
        join object1 in DataContext.SomeObject1 on obj.Id equals object1.ObjectId into object1items
        join object2 in DataContext.SomeObject2 on obj.Id equals object2.ObjectId into object2items
        where obj.OwnerId = ownerId
        orderby obj.Name
        select new MyBigObjectItem
        {
            Id = obj.Id,
            Name = obj.Name,
            TypeId = obj.TypeId,
            IsFoo = obj.IsFoo,
            IsBar = obj.IsBar,
            HasObject1 = object1items.Any(),
            HasObject2 = object2items.Any()
        }
    )
    .Decompile() // Line to add
    .ToListAsync(cancellationToken);
}