私はEntityFramework.Core 7.0.0-rc1-finalを使ってシードメソッドを書いています。EF 7にAddOrUpdateはどうなりましたか?
DbSetのAddOrUpdateメソッドはどうなりましたか?
私はEntityFramework.Core 7.0.0-rc1-finalを使ってシードメソッドを書いています。EF 7にAddOrUpdateはどうなりましたか?
DbSetのAddOrUpdateメソッドはどうなりましたか?
実装が完了しました。問題を参照してください#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);
}
私は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);
}
}
これは実際には機能しません。なぜなら、EF Coreは通常のトラッキングエラーを投げるからです。 'find()'の後でEF Coreがエンティティの追跡を開始し、 'update'が呼び出されたときにエンティティをすでに追跡しているというエラーがスローされるようです。 EFが既にエンティティを追跡している可能性があり、そのことをDBに伝えることができるので、それは扱いにくいです。 –
私はEF Core 1.1プロジェクトでこれをチェックしたところ、うまくいきました。このコードは5月末から生産されています。 – Tjaart
私はDouglas Gaskellに同意します。このソリューションは、コンテキスト・レベルで変更トラッキングが無効になっている場合にのみ機能します。代替実装については、以下の私のソリューションを参照してください。 – MSC
:
この解決策は、ベースエンティティクラスが正当なオプションであると仮定すると、この問題のより簡単な解決策です。単純さは、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; }
}
私は、一致するプロパティを指定できる優れたソリューションを見つけました。ただし、単一のエンティティではなく、各呼び出しのリストを使用します。それはあなたに良い古いもののように動作するより良いバージョンを実装する方法のヒントを与えるかもしれません。
は
既存のすべてのレコードをメモリにロードしますが、スケーラブルではありません –
もっと良い方法はありますか? ;) – SuperJMN
https://github.com/mcshaz/PicuCalendars/blob/master/PicuCalendars/DataAccess/EFExtensions.csは、[エンティティの重複チェックをどこで実行するか]です(// stackoverflow.com/a/16647237 )、 –
あなたの答えは1日付と半年前れます。それはまだ実装を待っていますか?それはすべて一緒にドロップされているようだ... –
シードデータの代替設計を思いついたので、その一部として実装されません。他の問題はまだ可能性があります。 – bricelam
こんにちは@bricelam、今日、EFコアにデータをシードするための代替手段はありますか?あなたはそれらを共有できますか? – octavioccl