2016-03-24 6 views

答えて

12

実装が完了しました。問題を参照してください#629 & #4526私はこれが何をしたいと思い

public static void AddOrUpdate<T>(this DbSet<T> dbSet, T data) where T : class 
     { 
      var t = typeof(T); 
      PropertyInfo keyField = null; 
      foreach (var propt in t.GetProperties()) 
      { 
       var keyAttr = propt.GetCustomAttribute<KeyAttribute>(); 
       if (keyAttr != null) 
       { 
        keyField = propt; 
        break; // assume no composite keys 
       } 
      } 
      if (keyField == null) 
      { 
       throw new Exception($"{t.FullName} does not have a KeyAttribute field. Unable to exec AddOrUpdate call."); 
      } 
      var keyVal = keyField.GetValue(data); 
      var dbVal = dbSet.Find(keyVal); 
      if (dbVal != null) 
      { 
       dbSet.Update(data); 
       return; 
      } 
      dbSet.Add(data); 
     } 
+1

あなたの答えは1日付と半年前れます。それはまだ実装を待っていますか?それはすべて一緒にドロップされているようだ... –

+1

シードデータの代替設計を思いついたので、その一部として実装されません。他の問題はまだ可能性があります。 – bricelam

+0

こんにちは@bricelam、今日、EFコアにデータをシードするための代替手段はありますか?あなたはそれらを共有できますか? – octavioccl

2

私はEFのコアへの移行のために私たちのコードベースにパッチを適用するために作成され、この拡張メソッドを使用することができます。

public static class DbSetExtension 
{ 
    public static void AddOrUpdate<T>(this DbSet<T> dbSet, T data) where T : class 
    { 
     var context = dbSet.GetContext(); 
     var ids = context.Model.FindEntityType(typeof(T)).FindPrimaryKey().Properties.Select(x => x.Name); 

     var t = typeof(T); 
     List<PropertyInfo> keyFields = new List<PropertyInfo>(); 

     foreach (var propt in t.GetProperties()) 
     { 
      var keyAttr = ids.Contains(propt.Name); 
      if (keyAttr) 
      { 
       keyFields.Add(propt); 
      } 
     } 
     if (keyFields.Count <= 0) 
     { 
      throw new Exception($"{t.FullName} does not have a KeyAttribute field. Unable to exec AddOrUpdate call."); 
     } 
     var entities = dbSet.AsNoTracking().ToList(); 
     foreach (var keyField in keyFields) 
     { 
      var keyVal = keyField.GetValue(data); 
      entities = entities.Where(p => p.GetType().GetProperty(keyField.Name).GetValue(p).Equals(keyVal)).ToList(); 
     } 
     var dbVal = entities.FirstOrDefault(); 
     if (dbVal != null) 
     { 
      context.Entry(dbVal).CurrentValues.SetValues(data); 
      context.Entry(dbVal).State = EntityState.Modified; 
      return; 
     } 
     dbSet.Add(data); 
    } 

    public static void AddOrUpdate<T>(this DbSet<T> dbSet, Expression<Func<T, object>> key, T data) where T : class 
    { 
     var context = dbSet.GetContext(); 
     var ids = context.Model.FindEntityType(typeof(T)).FindPrimaryKey().Properties.Select(x => x.Name); 
     var t = typeof(T); 
     var keyObject = key.Compile()(data); 
     PropertyInfo[] keyFields = keyObject.GetType().GetProperties().Select(p=>t.GetProperty(p.Name)).ToArray(); 
     if (keyFields == null) 
     { 
      throw new Exception($"{t.FullName} does not have a KeyAttribute field. Unable to exec AddOrUpdate call."); 
     } 
     var keyVals = keyFields.Select(p => p.GetValue(data)); 
     var entities = dbSet.AsNoTracking().ToList(); 
     int i = 0; 
     foreach (var keyVal in keyVals) 
     { 
      entities = entities.Where(p => p.GetType().GetProperty(keyFields[i].Name).GetValue(p).Equals(keyVal)).ToList(); 
      i++; 
     } 
     if (entities.Any()) 
     { 
      var dbVal = entities.FirstOrDefault(); 
      var keyAttrs = 
       data.GetType().GetProperties().Where(p => ids.Contains(p.Name)).ToList(); 
      if (keyAttrs.Any()) 
      { 
       foreach (var keyAttr in keyAttrs) 
       { 
        keyAttr.SetValue(data, 
         dbVal.GetType() 
          .GetProperties() 
          .FirstOrDefault(p => p.Name == keyAttr.Name) 
          .GetValue(dbVal)); 
       } 
       context.Entry(dbVal).CurrentValues.SetValues(data); 
       context.Entry(dbVal).State = EntityState.Modified; 
       return; 
      }     
     } 
     dbSet.Add(data); 
    } 
} 

public static class HackyDbSetGetContextTrick 
{ 
    public static DbContext GetContext<TEntity>(this DbSet<TEntity> dbSet) 
     where TEntity : class 
    { 
     return (DbContext)dbSet 
      .GetType().GetTypeInfo() 
      .GetField("_context", BindingFlags.NonPublic | BindingFlags.Instance) 
      .GetValue(dbSet); 
    } 
} 
+1

これは実際には機能しません。なぜなら、EF Coreは通常のトラッキングエラーを投げるからです。 'find()'の後でEF Coreがエンティティの追跡を開始し、 'update'が呼び出されたときにエンティティをすでに追跡しているというエラーがスローされるようです。 EFが既にエンティティを追跡している可能性があり、そのことをDBに伝えることができるので、それは扱いにくいです。 –

+0

私はEF Core 1.1プロジェクトでこれをチェックしたところ、うまくいきました。このコードは5月末から生産されています。 – Tjaart

+0

私はDouglas Gaskellに同意します。このソリューションは、コンテキスト・レベルで変更トラッキングが無効になっている場合にのみ機能します。代替実装については、以下の私のソリューションを参照してください。 – MSC

2

この解決策は、ベースエンティティクラスが正当なオプションであると仮定すると、この問題のより簡単な解決策です。単純さは、DomainEntityBaseを実装するドメインエンティティからもたらされます。これは、他の推奨されるソリューションの複雑さを軽減します。

public static class DbContextExtensions 
{ 
    public static void AddOrUpdate<T>(this DbSet<T> dbSet, IEnumerable<T> records) 
     where T : DomainEntityBase 
    { 
     foreach (var data in records) 
     { 
      var exists = dbSet.AsNoTracking().Any(x => x.Id == data.Id); 
      if (exists) 
      { 
       dbSet.Update(data); 
       continue; 
      } 
      dbSet.Add(data); 
     } 
    } 
} 

public class DomainEntityBase 
{ 
    [Key] 
    public Guid Id { get; set; } 
} 
+0

これは、すべてのモデルをそのベースから継承するように強制します。それはドメインにとっては良い考えではありません。 – SuperJMN

+0

良い点。シードテスト/参照データの追加または更新のみを使用し、シードするすべてのエンティティにはGuid識別子があることに注意してください。そのため、ユースケースでうまく機能しました。 – MSC

+0

最初の更新後にループから復帰します。それは意図的ですか? –

1

私は、一致するプロパティを指定できる優れたソリューションを見つけました。ただし、単一のエンティティではなく、各呼び出しのリストを使用します。それはあなたに良い古いもののように動作するより良いバージョンを実装する方法のヒントを与えるかもしれません。

https://github.com/aspnet/MusicStore/blob/7787e963dd0b7293ff95b28dcae92407231e0300/samples/MusicStore/Models/SampleData.cs#L48

+0

既存のすべてのレコードをメモリにロードしますが、スケーラブルではありません –

+0

もっと良い方法はありますか? ;) – SuperJMN

+0

https://github.com/mcshaz/PicuCalendars/blob/master/PicuCalendars/DataAccess/EFExtensions.csは、[エンティティの重複チェックをどこで実行するか]です(// stackoverflow.com/a/16647237 )、 –

0

拡張メソッドUpsertあります(コードは私のものではありません)。

context.Upsert(new Role { Name = "Employee", NormalizedName = "employee" }) 
     .On(r => new { r.Name }) 
     .Run(); 

On Github

関連する問題