我有一个泛型类,它对类型为T的实体执行添加/更新。AddOrUpdate()
方法接受DbSet
集合以及要添加或更新的项目列表。 DbSet
。 ItemExists()
用于检查集合中是否已存在某个项目。如果是,我们会更新。如果没有,我们补充说。该方法基本上将传入的项的主键与表中的每个项进行比较,如果匹配则返回true(以及数据库对象本身)。
该代码适用于具有少量记录的表。但是对于较大的表,ItemExists()
方法效率很低。该方法使用foreach循环,它本身在调用方法中的另一个foreach循环内,给出O(n ^ 2)。
更简单的方法是简单地使用contextDataSet.Contains(item)
,但会抛出一个说Unable to create a constant value of type
的异常,这是有道理的,因为EF无法将类转换为SQL查询。所以这不行。
现在我的实际问题是:有没有办法用传递的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;
}