c# - 使用 ValueInjecter 将 EntityFramework POCO 复制到 DTO 而不触发延迟加载列表和属性

标签 c# entity-framework entity-framework-4 valueinjecter

我在使用 ValueInjecter 将 EntityFramework POCO 的深度克隆创建到类似的 DTO 类时遇到问题。

如果我从具有多个相关实体/具有导航属性的子实体的复杂 POCO 对象注入(inject)到稍微简单的 DTO 中,ValueInjecter 似乎仍然会触及多个属性值并导致从数据库中延迟加载此数据。

我相信是 ValueInjecter 在准备将值注入(inject)指定目标时获取特定源对象中每个属性的值。

我的实际项目相当复杂,但作为一个例子,我以 NerdDinner 为例并以更简单的方式复制了这个问题。
(NerdDinner 是使用 EF4 (ScottGu NerdDinner Example) 的代码优先开发示例。

所以我有两个模型类。

public class Dinner
{
    public int DinnerId { get; set; }
    public string Title { get; set; }
    public DateTime EventDate { get; set; }
    public string Address { get; set; }
    public string HostedBy { get; set; }
    public virtual ICollection<RSVP> Rsvps { get; set; }
}


public class RSVP
{
    public int RsvpID { get; set; }
    public int DinnerID { get; set; }
    public string AttendeeEmail { get; set; }
    public virtual Dinner Dinner { get; set; }
}

我还创建了一个 DTO 类:
public class DinnerDTO
{ 
    public int DinnerId { get; set; }
    public string Title { get; set; }
    public DateTime EventDate { get; set; }
    public string Address { get; set; }
    public string HostedBy { get; set; }
}

备注 我没有在 Dinner 中找到的 Rsvps 集合在我的DinnerDTO .

同样重要的是,我使用 CloneInjection 约定来深度克隆对象。在 SO 以及许多其他站点上都建议使用此代码作为执行深度克隆注入(inject)的方法。
此代码可在此处找到:CloneInjection Code

现在,为了强调发生的延迟加载,我去插入了 10,000 个 RSVP 用于 Id = 1 的晚餐。

然后我执行以下代码:
var dinner = nerdDinners.Dinners.Where(x => x.DinnerId == 1).FirstOrDefault();
DinnerDTO dinnerDTO = new DinnerDTO();
dinnerDTO.InjectFrom<CloneInjection>(dinner);

如果我在 InjectFrom 行设置断点,然后跨过它,因为它会延迟加载 10,000 个 RSVP,所以会有相当大的延迟。如果我还在 Match 的 CloneInjection 代码中设置断点和 SetValue方法,在加载滞后解决之前,这些都不会被命中。这告诉我它一定是 ValueInjecter 内部的东西导致了 RSVPs 的延迟加载。属性(property)。

现在,如果我将上面的代码修改为:(在查询中添加 Include)
var dinner = nerdDinners.Dinners.Where(x => x.DinnerId == 1).Include("RSVPs").FirstOrDefault();
DinnerDTO dinnerDTO = new DinnerDTO();
dinnerDTO.InjectFrom<CloneInjection>(dinner);

此更改强制 RSVP 列表的“急切加载”,并且正如预期的那样,滞后与查询一致,InjectFrom线滴答作响,完全没有滞后。

我已经阅读了 StackOverflow 上一些模糊相关的帖子,其中一些建议在数据上下文上禁用然后启用 LazyLoading。我试过了,虽然它确实有效,但感觉很脏。

我通读了这篇文章 (Copying NHibernate POCO to DTO without triggering lazy load or eager load) 和相关代码,他的方法似乎是使用一些 NHibernate 方法来确定一个属性是否是未初始化的代理,并以某种方式将它们删除。我一直无法在 EF4 中找到类似的东西。

真正让我感到困惑的部分是,Rsvps 集合甚至不在我的 DTO 对象中,我什至对它的值(value)都不感兴趣。
这对我来说似乎不正确。我认为 ValueInjecter 代码不应该询问目标对象甚至可能不关心的属性值。

有什么方法可以覆盖 ValueInjecter 中的这种行为吗?以某种方式推迟对属性值的评估,直到绝对确定我想要该值,例如在 SetValueConventionInjection 的方法?那么至少它不会评估我的 DTO 甚至不想要的属性。

我能想到的最佳解决方案是让 ValueInjecter 或自定义约定以某种方式能够检测到已卸载的延迟加载属性,而不是对其进行评估,而是将目标上的该属性设置为 null。我不认为这是可能的。

我应该使用通过 EF 的更好方法吗?我不想急切地加载数据库中的所有内容。

我完全关闭了,问题根本不在于 ValueInjecter 吗?

* 编辑 *
我找到了解决方案并回答了这个问题,我仍然很好奇我是否只是做错了,或者是否还有更好的方法。

最佳答案

我觉得我为自己的问题找到了一个令人满意的答案,所以我打算自己解决这个问题。
我最终做了两件事。

首先,我在 dbContext 上完全禁用了延迟加载。
dbContext 构造函数中的类似内容。
this.Configuration.LazyLoadingEnabled = false;
我并没有真正使用 EF 的延迟加载功能,所以关闭它并没有什么大的损失。这只是意味着如果我想要填充相关实体,我必须在 Include 中指定它们。在我的查询中。没什么大不了的。

我做的另一件事是修改深度克隆注入(inject)约定以使用 SmartConventionInjection代码在这里找到SmartConventionInjection source .除了比基础注入(inject)更快之外,它也不会触及属性值,直到 SetValue调用,所以即使我确实有一些延迟加载属性,除非 DTO 也具有该属性,否则它们不会被触及。

关于c# - 使用 ValueInjecter 将 EntityFramework POCO 复制到 DTO 而不触发延迟加载列表和属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14793800/

相关文章:

c# - 在 .NET Compact 中模拟曲线图

asp.net-mvc - MVC + EF4 + POCO - 如何存储实体上下文?

sql - 如何在 C# 中的 codefirst 技术中使用带有 EF 的 Linq 避免 SQL 注入(inject)

c# - 如何提高EF中查询的执行速度

c# - Serializable transactions 是否只锁定其他 transactionscope 语句?还是一切?

entity-framework-4 - 如何在 Entity Framework 中使实体的集合成为集合?

.net - 检测 Entity Framework EntityObject 上的哪些属性发生了变化

c# - [Location1].DistanceTo[Location2] 中的单位

c# - 在 Active Directory : A device attached to the system is not functioning 中创建用户

c# - 需要在 5000 多个数据库上运行 SQL 脚本。我应该如何处理?