タイプ T のエンティティに対して追加/更新を実行するジェネリック クラスがあります。このAddOrUpdate()
メソッドは、DbSet
アクションを実行するコレクションと、DbSet
. ItemExists()
コレクション内に項目がすでに存在するかどうかを確認するために使用されます。その場合は、更新します。そうでない場合は、追加します。メソッドは基本的に、渡された項目の主キーをテーブル内のすべての項目と比較し、一致する場合は true (およびデータベース オブジェクト自体) を返します。
このコードは、レコード数が少ないテーブルでは正常に機能します。ただし、より大きなテーブルの場合、このItemExists()
方法は非常に非効率的です。このメソッドは、呼び出し元メソッドの別の foreach ループ内にある foreach ループを使用し、O(n^2) を与えます。
簡単な方法は単純に を使用することですが、EF はクラスを SQL クエリに変換できないため、意味contextDataSet.Contains(item)
のある例外がスローされます。Unable to create a constant value of type
だから、それはいけません。
今、私の実際の質問:渡された全体DbSet<T>
を置き換える方法はありますか? IEnumerable<T>
渡される IEnumerable はビューのデータグリッドにバインドされ、基本的にすべてのアイテムが含まれるため、論理的に言えば、コレクション全体を置き換えることは安全です。どんな助けでも大歓迎です。
コード
public void AddOrUpdate<I, P>(Expression<Func<I, P>> dbSetExpression, IEnumerable<T> itemsToUpdate)
where I : DbContext, new()
where P : DbSet<T>
{
DataFactory.PerformOperation<I>(c =>
{
if (m_primaryKey == null && !TryFindPrimaryKey(c))
{
throw new ArgumentException("Primary key cannot be null.");
}
// Get the table name from expression passed in.
string dbsetName = ((MemberExpression)dbSetExpression.Body).Member.Name;
var propertyInfo = c.GetType().GetProperties().Single(p => p.Name == dbsetName);
// Get the values in the table.
DbSet<T> contextDataSet = propertyInfo.GetValue(c) as DbSet<T>;
foreach (var item in itemsToUpdate)
{
// If the primary key already exists, we're updating. Otherwise we're adding a new entity.
T existingItem;
if (ItemExists(contextDataSet, item, out existingItem) && existingItem != null)
{
c.Entry(existingItem).CurrentValues.SetValues(item);
}
else
{
contextDataSet.Add(item);
}
}
c.SaveChanges();
});
}
private bool ItemExists(DbSet<T> itemInDbSet, T itemInList, out T existingItem)
{
foreach (var dbItem in itemInDbSet)
{
// Get the primary key value in the database.
var dbValue = dbItem.GetType().GetProperties().Single(
p => p.Name == m_primaryKey).GetValue(dbItem);
// Get the primary key value from the item passed in.
var itemValue =
itemInList.GetType().GetProperties().Single(
p => p.Name == m_primaryKey).GetValue(itemInList);
// Compare the two values.
if (dbValue.ToString() == itemValue.ToString())
{
existingItem = dbItem;
return true;
}
}
existingItem = null;
return false;
}