我喜欢AddOrUpdate允许您指定要检查的过滤器以避免添加重复项的事实。但我想要没有更新的类似功能。
现在我做这样的事情:
var checkProfile = from p in db.Profile
where p => p.LastName == newProfile.lastName
&& p => p.FirstName == newProfile.firstName
&& p => p.Middle== newProfile.middle
select p;
if (checkProfile.FirstOrDefault() == null)
{
db.Profile.Add(newProfile);
db.SaveChanges();
}
我知道我可以这样做:
db.Profile.AddOrUpdate(p => new {p.LastName, p.FirstName, p.Middle}, newProfile);
db.SaveChanges();
但我宁愿在这种情况下跳过修改数据。
第一个例子做了我想要的但有更多代码。在第一个例子中,有没有更简单/更清洁的方法来做我想要的事情?
更新
我喜欢Ognyan Dimitrov的建议。我正在努力实现它。我的模型继承自BaseEntity。我可以在那里放一个通用版本吗?
我的模型已定义:
public class Address :BaseEntity
{
我的BaseEntity:
public class BaseEntity
{
public virtual T AddIfNotExists<T>(T entity, Expression<Func<T, bool>> predicate = null)
{
var exists = predicate != null ? DbSet.Any(predicate) : DbSet.Any();
return !exists ? DbSet.Add(entity) : null;
}
}
我收到Any(...)和Add(...)的错误。 Add(...)的错误是'非静态字段,方法或属性'System.Data.Entity.DbSet.Add(object)''
需要对象引用我应该使用这个。添加(对象)?
更新2:
我已经创建了这段代码:
public static class DbSetExtensions
{
public static T AddIfNotExists<T>(this DbSet<T> dbSet, T entity, Expression<Func<T, bool>> predicate = null) where T : class, new()
{
var exists = predicate != null ? dbSet.Any(predicate) : dbSet.Any();
return !exists ? dbSet.Add(entity) : null;
}
}
现在我试着这样称呼它,但这不正确。原谅我缺乏理解。
_db.ProfileIdentifier.AddIfNotExists(newIdentifier,
pi => new {pi.ProfileId, pi.ProfileIdentifierTypeId, pi.ProfileIdentifierValue});
更新 - 解决方案:
我可以像这样调用DbSetextensions:
_db.ProfileIdentifier.AddIfNotExists(newIdentifier,
pi => pi.ProfileId == profileId &&
pi.ProfileIdentifierTypeId == (int)type &&
pi.ProfileIdentifierValue == value);
非常感谢与我合作Ognyan !!!
答案 0 :(得分:32)
您是否尝试检查实体是否存在,如果不存在 - 添加它?像这样:
<强>更新强>
class Blah < ActiveRecord::Base
scope :no_scope, -> {all}
scope :for_user, -> (user_id) {where(user_id: user_id)}
end
Blah.no_scope.for_user(1) # where(user_id: 1)
您可以直接使用此方法,并记住在调用后调用DbContext.SaveChanges()。
答案 1 :(得分:6)
我用了类似的东西,阅读这两篇帖子来制作我的代码。 我希望帮助那些需要与AddOrUpdate类似签名的人。
Entity Framework Add if not exist without update
Making AddOrUpdate change only some properties
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace System.Data.Entity.Migrations
{
//
// Summary:
// Metodos de extensão para System.Data.Entity.IDbSet
public static class DbSetMigrationsGustavoExtensions
{
/// <summary>
/// Adiciona uma entidade se ela não existe ainda
/// Assinatura semelhante ao AddOrUpdate
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="set">Set onde serão adicionadas as entidades</param>
/// <param name="identifierExpression">Campos usados na comparação</param>
/// <param name="entities">Entidades para adicionar</param>
public static void AddIfNotExists<TEntity>(this IDbSet<TEntity> set, Expression<Func<TEntity, object>> identifierExpression, params TEntity[] entities) where TEntity : class
{
var identifyingProperties = GetProperties<TEntity>(identifierExpression).ToList();
var parameter = Expression.Parameter(typeof(TEntity));
foreach (var entity in entities)
{
var matches = identifyingProperties.Select(pi => Expression.Equal(Expression.Property(parameter, pi.Name), Expression.Constant(pi.GetValue(entity, null))));
var matchExpression = matches.Aggregate<BinaryExpression, Expression>(null, (agg, v) => (agg == null) ? v : Expression.AndAlso(agg, v));
var predicate = Expression.Lambda<Func<TEntity, bool>>(matchExpression, new[] { parameter });
if (!set.Any(predicate))
{
set.Add(entity);
}
}
}
private static IEnumerable<PropertyInfo> GetProperties<T>(Expression<Func<T, object>> exp) where T : class
{
Debug.Assert(exp != null);
Debug.Assert(exp.Body != null);
Debug.Assert(exp.Parameters.Count == 1);
var type = typeof(T);
var properties = new List<PropertyInfo>();
if (exp.Body.NodeType == ExpressionType.MemberAccess)
{
var memExp = exp.Body as MemberExpression;
if (memExp != null && memExp.Member != null)
properties.Add(type.GetProperty(memExp.Member.Name));
}
else if (exp.Body.NodeType == ExpressionType.Convert)
{
var unaryExp = exp.Body as UnaryExpression;
if (unaryExp != null)
{
var propExp = unaryExp.Operand as MemberExpression;
if (propExp != null && propExp.Member != null)
properties.Add(type.GetProperty(propExp.Member.Name));
}
}
else if (exp.Body.NodeType == ExpressionType.New)
{
var newExp = exp.Body as NewExpression;
if (newExp != null)
properties.AddRange(newExp.Members.Select(x => type.GetProperty(x.Name)));
}
return properties.OfType<PropertyInfo>();
}
/// <summary>
/// Faz um set.Any(predicate)
/// Se não existe nada no set então adiciona
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="set">Set onde será adicionada a entidade</param>
/// <param name="predicate">Condição (exemplo: dbUser => dbUser.Nome == "Gustavo")</param>
/// <param name="entity">Entidade para adicionar</param>
/// <returns></returns>
public static T AddIfNotExists<T>(this IDbSet<T> set, Expression<Func<T, bool>> predicate, T entity) where T : class, new()
{
return !set.Any(predicate) ? set.Add(entity) : null;
}
}
}
答案 2 :(得分:5)
插入或更新模式
某些应用程序的常见模式是将实体添加为 new(导致数据库插入)或将实体附加为现有实体 并将其标记为已修改(导致数据库更新),具体取决于 主键的值。例如,使用数据库时 生成的整数主键通常用一个实体处理一个实体 零密钥作为新密钥和具有非零密钥的实体作为现有密钥。这个 可以通过基于检查设置实体状态来实现模式 主键值。
请注意,当您将状态更改为已修改的所有属性时 该实体将被标记为已修改且所有属性值都将被标记为 调用SaveChanges时发送到数据库。
context.Entry(blog).State = blog.BlogId == 0 ?
EntityState.Added :
EntityState.Modified;
context.SaveChanges();
答案 3 :(得分:4)
解决方案是可以的,当你只需要添加一个项目时,如果你必须添加多个项目,它在性能方面非常昂贵。 我认为有更好的解决方案:
public static class DbSetExtensions
{
public static EntityEntry<TEnt> AddIfNotExists<TEnt, TKey>(this DbSet<TEnt> dbSet, TEnt entity, Func<TEnt, TKey> predicate) where TEnt : class
{
var exists = dbSet.Any(c => predicate(entity).Equals(predicate(c)));
return exists
? null
: dbSet.Add(entity);
}
public static void AddRangeIfNotExists<TEnt, TKey>(this DbSet<TEnt> dbSet, IEnumerable<TEnt> entities, Func<TEnt, TKey> predicate) where TEnt : class
{
var entitiesExist = from ent in dbSet
where entities.Any(add => predicate(ent).Equals(predicate(add)))
select ent;
dbSet.AddRange(entities.Except(entitiesExist));
}
}
所以稍后它可以像这样使用:
using (var context = new MyDbContext())
{
var user1 = new User { Name = "Peter", Age = 32 };
context.Users.AddIfNotExists(user1, u => u.Name);
var user2 = new User { Name = "Joe", Age = 25 };
context.Users.AddIfNotExists(user2, u => u.Age);
// Adds user1 if there is no user with name "Peter"
// Adds user2 if there is no user with age 25
context.SaveChanges();
}
答案 4 :(得分:2)
所有其他答案都不正确。
“先写后读”可能会违反数据完整性,而不会放入事务控件中。
在SQL Server中,可以使用merge语句。但是,合并语句在EF中不可用。
答案 5 :(得分:1)
唯一想到的是使用IEqualityComparer<T>
,但这并没有真正停止工作,只是将其抽象出来并创建更清晰的代码。
答案 6 :(得分:1)
对我有用的是:
public static void AddIfNotExists<T>(this DbSet<T> dbSet, Func<T, object> predicate, params T [] entities) where T : class, new()
{
foreach (var entity in entities)
{
var newValues = predicate.Invoke(entity);
Expression<Func<T, bool>> compare = arg => predicate(arg).Equals(newValues);
var compiled = compare.Compile();
var existing = dbSet.FirstOrDefault(compiled);
if (existing == null)
{
dbSet.Add(entity);
}
}
}