5

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

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

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

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

所以我有两个模型类。

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

请注意,我DinnerDinnerDTO.

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

现在,为了强调发生的延迟加载,我去插入了 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。如果我还在 CloneInjection 代码中同时在MatchSetValue方法中设置断点,则在加载滞后解决之前,这些都不会被命中。这告诉我,它必须是 ValueInjecter 内部的东西,它会导致RSVPs属性的延迟加载。

现在,如果我将上面的代码修改为:(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。我试过了,虽然它确实有效,但感觉很脏。

我通读了这篇文章(将 NHibernate POCO 复制到 DTO 而不会触发延迟加载或急切加载)和相关代码,他的方法似乎是使用一些 NHibernate 方法来确定属性是否是未初始化的代理并以某种方式将它们剥离。我一直无法在 EF4 中找到类似的东西。

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

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

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

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

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

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

4

1 回答 1

5

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

首先,我在 dbContext 上完全禁用了延迟加载。dbContext 构造函数中的类似内容。

this.Configuration.LazyLoadingEnabled = false;

我并没有真正使用 EF 的延迟加载功能,所以关闭它并没有什么大的损失。这只是意味着如果我想要填充相关实体,我必须Include在我的查询中指定它们。没什么大不了。

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

于 2013-02-10T19:28:24.373 回答