将只读 属性 分配给 Entity Framework 中的新对象

Assigning Read Only Property to new object in Entity Framework

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

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

这是映射到实体

的我的class
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 */
}

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

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; }
}

它只是一个非常简单的 getter 和 setter 容器。

然后,对于我的 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 显示错误:

The specified type member 'IsFoo' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported." (System.NotSupportedException)

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

... or am I doomed to get the list of full objects and then transform those into my DTO?

差不多。在评估查询之前所做的任何事情都是 Entity Framework 可以转化为 SQL 的事情,这排除了任何自定义属性,例如您创建的自定义属性。您唯一真正的选择是创建一个存储过程来执行这些属性中包含的逻辑,只返回 IsFooIsBar 的结果布尔值,然后使用该 SP 检索您的对象。根据此查询的复杂性,无论如何这可能都是一个不错的举措。

您可以使用精彩的 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 查询上调用 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);
}