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

tho*_*ill 5 c# entity-framework entity-framework-4 valueinjecter

我在使用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; }
}
Run Code Online (Sandbox Code Playgroud)

public class RSVP
{
    public int RsvpID { get; set; }
    public int DinnerID { get; set; }
    public string AttendeeEmail { get; set; }
    public virtual Dinner Dinner { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我还创建了一个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; }
}
Run Code Online (Sandbox Code Playgroud)

请注意,我没有找到Dinner我的Rsvps集合DinnerDTO.

同样重要的是,我使用CloneInjection约定深度克隆对象.此处的代码既可以在SO上提供,也可以在许多其他站点中作为执行深度克隆注入的方法.此代码可在此处找到: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);
Run Code Online (Sandbox Code Playgroud)

如果我在行上设置一个断点InjectFrom,然后跳过它,那么它会延迟加载10,000 RSVP,因此存在相当大的延迟.如果我还在方法MatchSetValue方法中的CloneInjection代码中设置断点,则在加载延迟解决之后才会触发这些断点.这告诉我,它必定是ValueInjecter内部的东西,它会导致RSVPs属性的延迟负载.

现在,如果我修改上面的代码:(添加一个Include查询)

var dinner = nerdDinners.Dinners.Where(x => x.DinnerId == 1).Include("RSVPs").FirstOrDefault();
DinnerDTO dinnerDTO = new DinnerDTO();
dinnerDTO.InjectFrom<CloneInjection>(dinner);
Run Code Online (Sandbox Code Playgroud)

此更改强制RSVP列表的"Eager Load",并且正如预期的那样,滞后与查询一致,并且该InjectFrom行标记过去而没有任何延迟.

我已经阅读了StackOverflow上一些含糊不清的相关帖子,有些人建议禁用,然后在datacontext上启用LazyLoading.我试过了,虽然它确实有效,但感觉非常脏.

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

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

是否有一些方法可以在ValueInjecter中覆盖此行为?以某种方式推迟对属性值的评估,直到我绝对肯定我想要这个值,比如在SetValue方法中ConventionInjection?那么至少它不会评估我的DTO甚至不想要的属性.

我能想到的最好的解决方案是ValueInjecter或自定义约定,以某种方式能够检测到卸载的延迟加载属性,而不是评估它,而只是在目标上将该属性设置为null.我不认为这是可能的.

我应该使用EF有更好的方法吗?我不希望Eager在数据库中加载所有内容.

我完全离开了,问题根本不在ValueInjecter中吗?

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

tho*_*ill 5

我觉得我对自己的问题找到了一个满意的答案,所以我只想自己解决这个问题.我最后做了两件事.

首先,我完全在dbContext上禁用了Lazy Loading.dbContext构造函数中类似于此的东西.

this.Configuration.LazyLoadingEnabled = false;

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

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

  • 我现在可以吻你了!(以友好的非威胁方式) (3认同)