模型实体和外键对象

问题描述:

我有一个名为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,它是指向表运动的外键。

现在,我不能只用一个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>(); 

而且我有一个泛型化服务,CRUDs和实体模型或模式转换数据实体。一切工作正常,除了上创建,它的如下功能:

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),它返回一个SportDivisionModel对象没有运动的名称。在.ForMember(x => x.Sport, c => c.MapFrom(e => e.Sport.Name));中发现空引用异常。刚刚在数据库中创建条目后,它没有加载Sport。

我调试的代码,我看到有一个有效的SportId入口进入到我的数据库的表SportDivision,但是当我试图把它交给我的MVC应用程序,它并没有得到所有信息。

这只是一个关于创建的问题。如果我只是事先从数据库中获取数据,或者如果我编辑信息,那么我的模型对象中的Sport字段就会被填充。我不知道为什么会发生这种情况,我不能在我的通用服务调用中使用.Include(因为并非所有的BaseReferenceEntity类都有指向Sport的外键)。

请指教。提前致谢。

我必须扮演福尔摩斯,并尝试获得哪些可能是从你的问题的指示CreateOrUpdateFindById内容:

  • 你说你不使用Include因为通用服务的。我假设你也不使用显式加载(Load),因为你会面临同样的问题,你不能真正使它通用。

  • 结论:因为在SportDivisionSport导航属性在某些情况下(编辑)被加载,这只能发生因延迟加载。结论得到支持,Sport财产被标记为virtual

  • 延迟加载依赖于代理。如果您SportDivision实体是一个代理,那么

    1. 要么加载Sport实体工程
    2. ,或者你会得到一个异常告诉你的背景下已经设置(如果设置在上下文中)
  • 数2不是这种情况 - >结论:编号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方法问题引起的问题raw来自以及它是如何创建的。

  • 显然AutoMapper创建此行之后的实体:var raw = Mapper.Map<TModel, TEntity>(entry);

  • 如何Automapper创建一个实体?也许通过调用new TEntity或通过使用一些反射代码像Activator.CreateInstance或...

  • 它并不真正的问题是如何,但是可以肯定AutoMapper没有实例,从而不得不通过创建一个实体框架代理:

    var entity = context.Set<TEntity>().Create(); 
    

如果这一切都是真的,我觉得完全被AutoMapper和泛型过度所困扰。如果这一切都不会是一般的,我们可以解决了这个问题:

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; 

(我真的不知道,如果上面的Detached招确实有效。除此之外,你不得不从刚刚创建并保存的数据库中重新加载一个实体,并以某种方式保存它是愚蠢的。)

可能的技巧2号(无需从数据库重新加载,但对于更进一步的步骤的价格更难看) :

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();。还是有可能手动实例化对象,将它传递给AutoMapper,并且AutoMapper只更新对象的属性?

BTW:该生产线...

context.Entry(anotherRaw).CurrentValues.SetValues(raw); 

...是一种EF内置的 “AutoMapper” 的。 SetValues的参数是一般的System.Object(可能是您的...Model对象),并且该方法通过相同的属性名称将提供的对象的属性值映射到附加实体的属性。也许你可以以某种方式利用这个特性,而不是使用由AutoMapper完成的从模型到实体的映射。