Domanda

I'm trying to create a InsertOrUpdate function on an abstract layer on my generic repository, to be inherited by my concrete object repository:

This is my code:

 public abstract class GenericRepository<T> : IGenericRepository<T> where T : class
 {
    private readonly OhmioEntities context = new OhmioEntities();

    public IQueryable<T> FindBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
    {
        IQueryable<T> query = context.CreateObjectSet<T>().Where(predicate);
        return query;
    }

    public void InsertOrUpdate(T entity, System.Linq.Expressions.Expression<Func<T, bool>> Findpredicate)
    {   
        T _dbRecord = this.FindBy(Findpredicate).FirstOrDefault();
        if (_dbRecord != null)
        {
            // Edit the record
            _dbRecord = entity;
            context.ObjectStateManager.ChangeObjectState(entity, System.Data.EntityState.Modified);
        }
        else
        {
            // Agrego el registro
            context.CreateObjectSet<T>().AddObject(entity);
        }

        // Save Changes to DB
        context.SaveChanges();
    }
} 

I use it like this:

public class ClientsRepository : GenericRepository<Clients>
{
  Clients _cli = new Clients(){ClientID=1,ClienName="My New Client"};
  this.InsertOrUpdate(_cli, o => o.ClientID == 1);
}

The idea is that if ClientID=1 Existe, the records get edited. If not a new record is inserted. It works perfect on Insert new object, but it gives me error on this line:

context.ObjectStateManager.ChangeObjectState(entity, System.Data.EntityState.Modified);

That says:

{"ObjectStateManager do not contain a ObjectStateEntry with reference to a object of type 'DataLayer.Clients'."}

Why am i getting this? any clues? thanks!

UDATE

Well i get it working it just took some more code:

public void InsertOrUpdate(T entity, System.Linq.Expressions.Expression<Func<T, bool>> Findpredicate)
    {   
        T _dbRecord = this.FindBy(Findpredicate).FirstOrDefault();
        if (_dbRecord != null)
        {
            // Edit the object
            context.ObjectStateManager.ChangeObjectState(_dbRecord, System.Data.EntityState.Modified);
            context.Detach(_dbRecord);
            _dbRecord = entity;
            context.AttachTo(entity.ToString().Split('.')[2], _dbRecord);
            context.ObjectStateManager.ChangeObjectState(_dbRecord, System.Data.EntityState.Modified);                                
        }
        else
        {
            // add the object
            context.CreateObjectSet<T>().AddObject(entity);
        }

        // Save Changes to DB
        context.SaveChanges();
    }
È stato utile?

Soluzione

This load and detach trick in your UPDATE is not necessary. You can just only ask the database if the entity already exists or not without loading it (using Any() instead of FirstOrDefault()):

public void InsertOrUpdate(T entity, Expression<Func<T, bool>> Findpredicate)
{   
    bool exists = this.FindBy(Findpredicate).Any();
    if (exists)
    {
        context.CreateObjectSet<T>().Attach(entity);
        context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
    }
    else
    {
        context.CreateObjectSet<T>().AddObject(entity);
    }

    context.SaveChanges();
}

Alternatively you can load the original entity from the database and update its properties with the modified detached entity (with ApplyCurrentValues). It has the benefit that only the properties that have really changed are sent with the UPDATE statement to the database (but at the cost that the original entity must be loaded) while setting the whole entity state to Modified will also send the unchanged properties with the UPDATE to the DB (but with the benefit that the orginal doesn't need to be loaded):

public void InsertOrUpdate(T entity, Expression<Func<T, bool>> Findpredicate)
{   
    T _dbRecord = this.FindBy(Findpredicate).FirstOrDefault();
    if (_dbRecord != null)
    {
        context.CreateObjectSet<T>().ApplyCurrentValues(entity);
    }
    else
    {
        context.CreateObjectSet<T>().AddObject(entity);
    }

    context.SaveChanges();
}

Note that both solutions only work to update scalar properties of an entity. It doesn't work with changed relationships and navigation properties. There is no simple generic solution for this more general Update scenario with arbitrary object graphs.

Altri suggerimenti

You have created a new Client object, but I guess haven't added in the context yet. Try adding in context first then change its state to Modified.

Plus your FindBy function is being used just as a signal, maybe you can use Any function instead of actually loading the entity in this function and change the return type to boolean.

One more thing your example usage of your class is a bit weird. I am NOT sure whether we can directly call a method in a class like you are calling without having this method in some other method.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top