2012-03-21 11 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を求めている:私はSportDivisionModelにSportDivisionからデータを転送するautomapperを使用

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

と逆に。マッピングは次のようになります。

Mapper.CreateMap<SportDivision, SportDivisionModel>() 
     .ForMember(x => x.Sport, c => c.MapFrom(e => e.Sport.Name)); 
Mapper.CreateMap<SportDivisionModel, SportDivision>(); 

そして私はCRUDsとエンティティにモデル化するためのエンティティまたはモデルからのデータを変換genericizedサービスを提供しています。すべてが機能を以下に示し、そのうちの作成を除いて正常に動作します:あなたがdm.FindById(created.Id)を参照してください非常に最後の行では、

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

は、それが無いスポーツ名でSportDivisionModelオブジェクトを返します。 null参照例外は.ForMember(x => x.Sport, c => c.MapFrom(e => e.Sport.Name));にあります。エントリがデータベースに作成された後、Sportは読み込まれませんでした。

コードをデバッグしましたが、有効なSportIdのエントリが自分のデータベースのSportDivisionテーブルに入力されていることがわかりましたが、MVCアプリケーションに渡すと、すべて取得されません情報。

これは作成時の問題です。事前にデータベースを作成せずにデータベースからデータを取得するだけの場合、または情報を編集した場合、モデルオブジェクトのSportフィールドにデータが入力されます。私はこれがなぜ起こっているのか分かりませんし、(BaseReferenceEntityのすべてのクラスがSportを指す外部キーを持っているわけではないので)genericサービスコールで.Includeを使用することはできません。

お知らせください。前もって感謝します。

答えて

2

私はシャーロック・ホームズを再生し、あなたの問題の兆候からCreateOrUpdateFindByIdの内容何ができるかを導出しようとする必要があります:あなたはので、一般的なサービスのIncludeを使用しないことを言う

  • 。私は、明示的な読み込み(Load)も使用しないことを前提としています。これは、あなたが実際にそれを一般的にすることはできないという同じ問題に直面するからです。

  • 結論:SportDivisionSportナビゲーションプロパティが特定のシナリオ(編集)でロードされるため、これは遅延読み込みのためにのみ発生する可能性があります。結論は、Sportプロパティがvirtualとマークされているという事実に裏付けられています。

  • 遅延読み込みはプロキシに依存します。あなたのSportDivisionエンティティはプロキシである場合、

    1. いずれかSportエンティティをロードする
    2. に動作しますか、あなたは(あなたがコンテキストに配置している場合)
  • コンテキストがすでに配置されていることを通知する例外を取得します

    ナンバー2はそうではありません - >結論:ナンバー1は、前提条件が満たされている場合場合でなければなりません

  • しかし、あなたのSportDivisionエンティティはプロキシであることを前提条件が真ではない:ナンバー1も

  • 結論(ロードSportない仕事をする)場合ではありません。

  • So: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を直接のメソッドに渡すと、質問が発生しますrawそれはどのように作られているのでしょうか。明らかに

  • AutoMapperは、この行の後にエンティティを作成します:var raw = Mapper.Map<TModel, TEntity>(entry);

  • どうAutomapperはエンティティを作成しますか?

    :おそらくnew TEntityまたはActivator.CreateInstanceか...

  • のようないくつかの反射コードを使用することにより、それが本当にどんなに、しかし確かAutoMapperにより作成されなければならなかったEntity Frameworkのプロキシをインスタンス化しないためにしませんを呼び出すことにより、

    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(DBからリロードせずに、さらに醜いステップになるための価格) :

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はオブジェクトのプロパティを更新するだけですか?ところで

:行...

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

は... EFの組み込み "AutoMapper" の一種です。 SetValuesのパラメータは一般的にSystem.Object...Modelオブジェクトでもよい)であり、このメソッドは、指定されたオブジェクトのプロパティ値を、同じプロパティ名で添付エンティティのプロパティにマップします。おそらく、AutoMapperによって行われたモデルからエンティティへのマッピングを使用する代わりに、この機能を何らかの形で活用することができます。

関連する問題