我有一个名为SportDivision的EF对象。为简单起见,我不会包括每个领域,只包括那些相关的领域:
[Table("SportDivision", Schema = "dbo")]
public class SportDivision: BaseReferenceEntity
{
public int Id { get; set; }
public string Name { get; set; }
public int SportId { get; set; }
[ForeignKey("SportId")]
public virtual Sport Sport { get; set; }
}
所以它有一个SportId,它是指向Sport表的外键。
现在,我不能在我的视图中使用EF对象,因此我有一个映射到SportDivision的模型类,名为SportDivisionModel:
public class SportDivisionModel: BaseReferenceModel
{
public int Id { get; set; }
public string Name { get; set; }
public int SportId { get; set; }
//Read only fields
public string Sport { get; set; }
}
我使用automapper将数据从SportDivision传输到SportDivisionModel,反之亦然。映射看起来像这样:
Mapper.CreateMap<SportDivision, SportDivisionModel>()
.ForMember(x => x.Sport, c => c.MapFrom(e => e.Sport.Name));
Mapper.CreateMap<SportDivisionModel, SportDivision>();
我有一个通用的服务,可以将数据从实体转换为模型或模型转换为实体。除了Create之外,一切正常,其功能如下所示:
public TModel Create<TModel, TEntity>(TModel entry)
where TModel : BaseReferenceModel
where TEntity : BaseReferenceEntity
{
var dm = ServiceLocator.Current.GetInstance<ICrudService<TEntity>>();
var raw = Mapper.Map<TModel, TEntity>(entry);
var created = dm.CreateOrUpdate(raw);
return Mapper.Map<TEntity, TModel>(dm.FindById(created.Id));
}
在最后一行,您看到dm.FindById(created.Id)
,它返回一个没有Sport名称的SportDivisionModel对象。在.ForMember(x => x.Sport, c => c.MapFrom(e => e.Sport.Name));
中可以找到空引用异常。在数据库中创建条目后,它没有加载Sport。
我调试了代码,我看到带有有效SportId的条目被输入到我的数据库的SportDivision表中,但是当我尝试将它带到我的MVC应用程序时,它并没有得到所有的信息。
这只是创建时的问题。如果我只是从数据库中获取数据而不事先创建数据,或者如果我编辑信息,那么我的模型对象中的Sport字段就会被填充。我不知道为什么会发生这种情况,我不能在我的通用服务调用中使用.Include(因为并非所有BaseReferenceEntity类都有一个指向Sport的外键)。
请指教。提前谢谢。
答案 0 :(得分:2)
我必须扮演夏洛克·福尔摩斯,并尝试从你问题中的适应症中得出CreateOrUpdate
和FindById
的内容:
您说由于通用服务,您不使用Include
。我假设你也没有使用显式加载(Load
),因为你会面临同样的问题,你不能真正使它成为通用的。
结论:由于Sport
中的SportDivision
导航属性在某些情况下被加载(编辑),这只会由于延迟加载而发生。结论得到以下事实的支持:Sport
属性标记为virtual
。
延迟加载依赖于代理。如果您的SportDivision
实体是代理,那么
Sport
实体2号不是这样 - &gt;结论:如果满足前提条件,则必须为 <1>
但是数字1也不是这样(加载Sport
不工作)
结论:您的SportDivision
实体是代理的前提条件不正确。
所以:SportDivision
不是代理。这是否意味着你在禁用的上下文中有延迟加载?否:因为您说编辑工作意味着当您从数据库加载实体时,它们作为代理加载并支持延迟加载。
编辑工作,延迟加载未被禁用,但是当您继续使用新创建的实体时,创建新实体不会像加载Sport
实体那样起作用。
结论:您新创建的实体(从CreateOrUpdate
返回)不是代理,而CreateOrUpdate
看起来与此类似:
public TEntity CreateOrUpdate(TEntity raw) where TEntity : class
{
if (blabla)
; //update
else
{
context.Set<TEntity>().Add(raw);
context.SaveChanges();
return raw;
}
}
和FindById
只是:
public TEntity FindById(int id)
{
return context.Set<TEntity>().Find(id);
}
由于您将raw
直接传递到Add
的{{1}}方法,问题会引发DbSet<T>
来自何处以及如何创建。< / p>
显然,AutoMapper会在此行之后创建实体:raw
Automapper如何创建实体?可能是通过调用var raw = Mapper.Map<TModel, TEntity>(entry);
或使用某些反映代码,例如new TEntity
或...
确实如何,但确保AutoMapper不会实例化必须由以下内容创建的实体框架代理:
Activator.CreateInstance
如果这一切都是真的,我觉得AutoMapper和一般的过度行为完全搞砸了。如果所有这些都不是通用的,我们可以通过以下方式解决问题:
var entity = context.Set<TEntity>().Create();
相反,我们现在必须尝试一些丑陋的技巧:
context.Set<SportDivision>().Add(raw);
context.SaveChanges();
context.Entry(raw).Reference(r => r.Sport).Load();
(我真的不确定上面的context.Set<TEntity>().Add(raw);
context.SaveChanges();
context.Entry(raw).State = EntityState.Detached;
// We hope that raw is now really out of the context
raw = context.Set<TEntity>().Find(raw.Id);
// raw must be materialized as a new object -> Hurray! We have a proxy!
return raw;
技巧是否有效。除此之外,你不得不从你刚创建并保存的数据库重新加载一个实体,这在某种程度上是愚蠢的。)
潜在的技巧2(没有从数据库重新加载,但价格更加丑陋):
Detached
AutoMapper是否具有“自定义分配器或实例化器”的功能,是否可以提供自定义用户数据(上下文)?然后有机会让AutoMapper调用context.Set<TEntity>().Add(raw);
context.SaveChanges();
context.Entry(raw).State = EntityState.Detached;
// We hope that raw is now really out of the context
var anotherRaw = context.Set<TEntity>().Create(); // Proxy!
anotherRaw.Id = raw.Id;
context.Set<TEntity>().Attach(anotherRaw);
context.Entry(anotherRaw).CurrentValues.SetValues(raw);
context.Entry(anotherRaw).State = EntityState.Unchanged;
return anotherRaw; // Proxy! Lazy loading will work!
。或者是否可以手动实例化对象,将其传递给AutoMapper,AutoMapper只是更新对象的属性?
顺便说一句:这句话......
context.Set<TEntity>().Create();
...是EF的内置“AutoMapper”。 context.Entry(anotherRaw).CurrentValues.SetValues(raw);
的参数是一般SetValues
(可能是您的System.Object
对象),该方法将属性值从提供的对象映射到相同属性名称的附加实体的属性。也许您可以某种方式利用此功能,而不是使用AutoMapper完成的从模型到实体的映射。