如何在附加之前检查实体是否存在?

时间:2014-06-08 01:51:31

标签: entity-framework ef-code-first entity-framework-6

当向系统输入新的Post时,需要实例化一些Tags并将其与Post实例相关联。其中一些Tags已经存在于数据库中,其他则不会并且需要插入。

一个例子:

var post = new Post {
    Slug = "hello-world",
    Title = "Hello, World!",
    Content = "this is my first post.",
    Tags = new List<Tag>()
};

var tag = new Tag { Name = "introduction" };
post.Tags.Add(tag);

当数据库中不存在关联的Tag时,我可以依赖对DbSet<T>.Add的简单调用来将post和关联的标记插入到db中。 但是,尝试插入包含已存在于db中的关联标记的帖子会导致标记表上发生主键冲突。

为了解决这个问题,我尝试将Attach每个标记添加到数据库上下文中,当数据库中已存在标记时,该数据库上下文会超级工作,否则会引发以下内部异常的异常:< / p>

  

INSERT语句与FOREIGN KEY约束冲突&#34; FK_dbo.TagPosts_dbo.Tags_Tag_Name&#34;。冲突发生在   数据库&#34; EF.Domain.BlogDb&#34;,表&#34; dbo.Tags&#34;,列   &#39;名称&#39 ;.声明已经终止。&#34;}

我想仅在需要时将与帖子关联的标签插入数据库。我怎样才能做到这一点?

1 个答案:

答案 0 :(得分:2)

你的困境非常有趣。如果您不使用通用存储库,但知道要附加的实体的主键的特定存储库,则可以使用如下代码:

var tagExists = Tags.Any(t => t.Name == tag.Name);

var tag = Tags.Find(tag.Name);

但是在您的情况下,我们需要一种更通用的方法,例如获取Repository类使用的实体的主键,而不管实体的类型。为实现这一点,我在DbContext类上创建了两个扩展方法:

public static IList<string> GetPrimaryKeyNames<TEntity>(this DbContext context)
    where TEntity : class
{
    var objectContext = ((IObjectContextAdapter)context).ObjectContext;
    var set = objectContext.CreateObjectSet<TEntity>();

    return set.EntitySet.ElementType
        .KeyMembers
        .Select(k => k.Name)
        .ToList();
}

public static IList<object> GetPrimaryKeyValues<TEntity>(this DbContext context, TEntity entity)
    where TEntity : class
{
    var valueList = new List<object>();
    var primaryKeyNames = context.GetPrimaryKeyNames<TEntity>();

    foreach(var propertyInfo in entity.GetType().GetProperties())
    {
        if (primaryKeyNames.Contains(propertyInfo.Name))
        {
            valueList.Add(propertyInfo.GetValue(entity));
        }
    }

    return valueList;
}

利用这些方法,您可以更改Repository类的Attach方法,如下所示:

public void Attach(TEntity entity)
{
    var storeEntity = _context.Set<TEntity>().Find(
       _context.GetPrimaryKeyValues(entity).ToArray());

    if (storeEntity != null)
    {
        _context.Entry(storeEntity).State = EntityState.Detached;
        _context.Set<TEntity>().Attach(entity);
    }
}