Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework Add if not exist without update

I like the fact that AddOrUpdate let's you specify a filter to check to avoid adding duplicates. But I would like similar functionality without the update.

Right now I do something like this:

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

I know I can do something like this:

db.Profile.AddOrUpdate(p => new {p.LastName, p.FirstName, p.Middle}, newProfile);
db.SaveChanges();

But I would rather skip modifying the data in this case.

The first example does what I want but with more code. Is there a simpler/cleaner way to do what I want in the first example?

Update:

I like Ognyan Dimitrov's suggestion. I'm trying to implement it. My models inherit from BaseEntity. Can I put a generic version of that there?

My model is defined:

public class Address :BaseEntity
{

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

I'm getting errors for Any(...) and Add(...). The error for Add(...) is 'An object reference is required for the non-static field, method, or property 'System.Data.Entity.DbSet.Add(object)' '

Should I be using this.Add(object) ?

Update 2:

I've created this code:

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

}

Now I'm trying to call it like this, but it's not correct. Forgive my lack of understanding.

_db.ProfileIdentifier.AddIfNotExists(newIdentifier,
            pi => new {pi.ProfileId, pi.ProfileIdentifierTypeId, pi.ProfileIdentifierValue});

Update - Solution:

I can call the DbSetextensions like this:

_db.ProfileIdentifier.AddIfNotExists(newIdentifier,
            pi => pi.ProfileId == profileId &&  
            pi.ProfileIdentifierTypeId == (int)type &&  
            pi.ProfileIdentifierValue == value);

Thanks a lot for working with me Ognyan!!!

like image 275
M Kenyon II Avatar asked Jul 01 '15 13:07

M Kenyon II


Video Answer


2 Answers

Have you tried to check if the entity exists and if not - add it? Like this :

UPDATE

using System.Linq.Expressions;
    public class ContextWithExtensionExample
    {
        public void DoSomeContextWork(DbContext context)
        {
            var uni = new Unicorn();
            context.Set<Unicorn>().AddIfNotExists(uni , x => x.Name == "James");
        }
    }

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

You can use this method directly and remember to call DbContext.SaveChanges() after the call.

like image 129
Ognyan Dimitrov Avatar answered Oct 09 '22 07:10

Ognyan Dimitrov


All the other answers are incorrect.

"Read before write" can violate data integrity without being put inside a transaction control.

In SQL Server, you can use merge statement. However merge statement is not available in EF.

like image 18
zs2020 Avatar answered Oct 09 '22 06:10

zs2020