将只读属性分配给实体框架中的新对象
Posted
技术标签:
【中文标题】将只读属性分配给实体框架中的新对象【英文标题】:Assigning Read Only Property to new object in Entity Framework 【发布时间】:2015-04-18 12:50:48 【问题描述】:我有一个映射到实体框架数据库的对象。这是一个相当大的对象,在获取它们的列表时,我只想要数据的一个子集。
问题是我需要两个只读属性,它们在 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 正在检索的数据量。为此,我们创建了一个未映射到实体的新 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;
它只是一个非常简单的 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);
该查询中有很多内容,但我想给出一个准确的例子来说明我在做什么。我们正在加入另外两个我们有一对多关系的对象。我只需要知道是否存在任何关系。
问题在于 Intellitrace 显示错误的 IsFoo = obj.IsFoo,
和 IsBar = obj.IsBar,
行:
LINQ to Entities 不支持指定的类型成员“IsFoo”。仅支持初始化程序、实体成员和实体导航属性。” (System.NotSupportedException)
关于如何完成这项工作的任何想法,或者我注定要获得完整对象的列表,然后将它们转换为我的 DTO?
【问题讨论】:
【参考方案1】:...还是我注定要获得完整对象的列表,然后将它们转换为我的 DTO?
差不多。在评估查询之前所做的任何事情都是 Entity Framework 可以转换为 SQL 的,这排除了任何自定义属性,例如您创建的那些。您唯一真正的选择是创建一个存储过程来执行这些属性中包含的逻辑,只返回IsFoo
和IsBar
的结果布尔值,然后使用该SP 来检索您的对象。根据此查询的复杂性,无论如何这可能是一个不错的举措。
【讨论】:
我最终创建了一个静态扩展类,它接受 TypeId 并返回 IsFoo 和 IsBar 逻辑的结果。那么MyBigObject
和MyBigObjectItem
都在只读属性中使用静态类,TypeId是从Entity中设置的。【参考方案2】:
您可以使用出色的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);
【讨论】:
以上是关于将只读属性分配给实体框架中的新对象的主要内容,如果未能解决你的问题,请参考以下文章
Vue导入导致无法分配给对象错误的只读属性'exports'