2012-03-21 71 views
1

我有一個名爲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的外鍵)。

請指教。提前致謝。

回答

2

我必須扮演福爾摩斯,並嘗試獲得哪些可能是從你的問題的指示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完成的從模型到實體的映射。