Entity Framework 7, Insert or Update

4

I've been looking for a method to check if an object exists on the base, if it exists, execute a update , if not, insert .

I did not find anything that would answer me, so I made the following code:

public static void InsertOrUpdate<T>(this DbSet<T> dbSet, T entity) where T : class
{
    PropertyInfo pId = entity.GetType().GetProperty("Id");
    if (pId != null)
    {
        object valId = pId.GetValue(entity);
        if (dbSet.Any(p => p.GetType().GetProperty("Id").GetValue(p).ToString() == valId.ToString()))
        {
            T e = dbSet.Where(p => p.GetType().GetProperty("Id").GetValue(p).ToString() == valId.ToString()).FirstOrDefault();

            foreach (PropertyInfo p in e.GetType().GetProperties().Where(x => x.CanWrite && x.Name != "Id"))
            {
                p.SetValue(e, p.GetValue(entity));
            }

            dbSet.Update(e);
        }
        else
        {
            dbSet.Add(entity);
        }
    }
}

As I always read something about handling reflection, and I try to optimize the code, the question is simple:

Is there another way to do this method, or to improve this code?

  

At first, I have no problem with it, and it works perfectly. It's just a matter of not messing around and optimizing the code.

I'm using ASP.NET Core with Entity Framework Core and I have no practice with these.

Edit:

I've got this other method ( looks better but still has reflection ):

public static void InsertOrUpdate<T>(this ApplicationDbContext context, T entity) where T : class
{
    PropertyInfo ps = entity.GetType().GetProperty("Id");
    if (ps != null)
    {
        DbSet<T> set = context.Set<T>();
        if (set.Any(x => ps.GetValue(x).ToString() == ps.GetValue(entity).ToString()))
        {
            context.Entry(entity).State = EntityState.Modified;
        }
        else
        {
            set.Add(entity);
        }
    }
}
    
asked by anonymous 17.12.2017 / 23:32

1 answer

1

Create a class to create an extension method with the name InsertOrUpdate<T> generic, in that code if you first search the primary key (s) of your settings that were made for your Model with this the search in your table is made to see if the data has already been persisted and with this information it makes the decision if it is Add or Update that should be done, example / strong>:

public static class Utils
{
    public static void InsertOrUpdate<T>(this DbContext context, T model)
        where T: class
    {
        EntityEntry<T> entry = context.Entry(model);            
        IKey primaryKey = entry.Metadata.FindPrimaryKey();            
        if (primaryKey != null)
        {
            object[] keys = primaryKey.Properties.Select(x => x.FieldInfo.GetValue(model))
                                            .ToArray();
            T result = context.Find<T>(keys);
            if (result == null)
            {
                context.Add(model);
            }
            else
            {
                context.Entry(result).State = EntityState.Detached;
                context.Update(model);
            }
        }
    }
}

How to use:

using (DatabaseContext db = new DatabaseContext())
{
    Cliente c = new Cliente();                
    c.Nome = "StackOverFlow";               

    db.InsertOrUpdate(c);
    db.SaveChanges();

}

Note: This example will always generate a SQL to fetch information ( whether it exists or not) and another SQL can be between INSERT or UPDATE , could even check simpler where the property value was 0 , would then make a INSERT and greater than 0 would make a UPDATE , but, that would only to work with% autocomplete with an auto increment key, the above method works even for a key that has more than one field and is not incremented by the bank and it is also worth remembering that it is always ideal to set your Model in Model mode %, like example below:

Fluent :

public class Cliente
{
    public int Id { get; set; }
    public string Nome { get; set; }
}
public class Test
{        
    public int Chave1 { get; set; }        
    public int Chave2 { get; set; }
    public decimal Valor { get; set; }
}

Model :

public class DatabaseContext : DbContext
{
    public DbSet<Cliente> Cliente { get; set; }
    public DbSet<Test> Test { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Cliente>()
            .ToTable("Clientes")
            .HasKey(x => x.Id);

        modelBuilder.Entity<Cliente>()
            .Property(x => x.Id)
            .IsRequired()
            .UseSqlServerIdentityColumn();

        modelBuilder.Entity<Cliente>()
            .Property(x => x.Nome)
            .HasMaxLength(50)
            .IsRequired();

        modelBuilder.Entity<Test>()
            .ToTable("Test")
            .HasKey(x => new { x.Chave1, x.Chave2 });

        modelBuilder.Entity<Test>()
           .Property(x => x.Chave1)
           .IsRequired();

        modelBuilder.Entity<Test>()
            .Property(x => x.Chave2)
            .IsRequired();

        modelBuilder.Entity<Test>()
            .Property(x => x.Valor)
            .IsRequired();

    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("connection_strings", options =>
        {
        });
    }
}

If you still want to check if your key has the value Context and in that case it would give a 0 and if the value is greater than INSERT would give 0 the extension method would change to that:

public static void InsertOrUpdate<T>(this DbContext context, T model)
    where T : class
{
    EntityEntry<T> entry = context.Entry(model);
    IKey primaryKey = entry.Metadata.FindPrimaryKey();
    if (primaryKey != null)
    {
        object key = primaryKey.Properties.Select(x => x.FieldInfo.GetValue(model))
                                          .FirstOrDefault();                
        if (key.ToString() == "0")
        {
            context.Add(model);
        }
        else
        {                    
            context.Update(model);
        }
    }
}

Only this code would work as already spoken only for primary key with an auto increment field.

Note: The big problem with the code that is in your question is that the value of the primary key is fixed and this always requires the primary key of your table with the name of UPDATE , what is in the response can be any name, just needs to be configured as explained above.

18.12.2017 / 01:59