How to Check If Entity Exists Before Attaching?

ef-code-first entity-framework entity-framework-6

Question

once a newPost is entered into the system, severalTags will need to be launched and connected to thePost instance. certain of theseTags some will need to be inserted while others will already be in the database.

a case in point

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);

When a connectedTag is not in the database, I can rely on a straightforward call toDbSet<T>.Add to add the post and its corresponding tags to the database. A primary key violation on the tags table results from attempting to insert a post with related tags that already exist in the database.

I made an effort to remedy this issue byAttach If the tags already exist in the database, adding them to the database context works great; otherwise, an exception is thrown with the inner exception listed below:

The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.TagPosts_dbo.Tags_Tag_Name". The conflict occurred in database "EF.Domain.BlogDb", table "dbo.Tags", column 'Name'. The statement has been terminated."}

Only when necessary, I want to insert post-related tags into the database. How do I accomplish this?

1
0
6/8/2014 1:51:30 AM

Accepted Answer

Your situation is pretty intriguing. You could just use a code like this if you used a specific repository instead of a generic one and knew what the primary key of the entity you wanted to attach was:

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

or

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

The best course of action in your situation would be to obtain the main key of the entity that the Repository class uses, independent of the object's nature. I've added two additional methods to the DbContext class to accomplish this:

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;
}

With the use of these techniques, you can modify the Attach method on the Repository class as follows:

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);
    }
}
3
6/9/2014 3:12:51 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow