0

Suppose that I have two entities E1 and E2 both share the same Code value. Suppose that I want to delete the first and insert the next:

Item.Delete(E1);
Item.Insert(E2);
Item.Save();

Where

public void Delete(Entity E) 
{
    var existingEntity = _context.EntityTable.SingleOrDefault(s => s.Code == E.Code);
    _context.EntityTable.Remove(existingEntity);
}

public void Insert(Entity E)
{
    var existingEntity = _context.EntityTable.FirstOrDefault(s => s.Code == E.Code);
    if (existingEntity != null){
        throw new ArgumentException("Item alread exists.")
    }

    var newEntity = CreateDbEntity(E); // Create Db Entity just convert the type. Nothing much here.
    _context.EntityTable.Add(newEntity);
}

public void Save()
{
    _context.SaveChanges();
}

The problem here is that when I remove E1 from the EntityTable in the _context, that is not reflected immediately until I save. Thus, the operation will fail because inserting E2 sill not be successful since E1 is still there. Is there away work around that, where EntityTable does reflect the changes that have been made?

Husain
  • 784
  • 1
  • 9
  • 22
  • 1
    why don't you leave the unique condition to SQL and not check in code? – johnny 5 Aug 10 '17 at 18:09
  • Between your query whether one exists and your call to SaveChanges(), one can also be inserted by another process or thread. Don't do this in code. – CodeCaster Aug 10 '17 at 18:13
  • delete > save > insert > save.. lol – Nikhil Vartak Aug 10 '17 at 18:16
  • @johnny5 This example is simplistic. But in the case where one has soft delete, then unique condition in SQL wouldn't work. – Husain Aug 10 '17 at 19:44
  • @niksofteng Haha true. The idea here is to go to the database only once. I was trying to see if there is a smart way of doing this. Otherwise, I will fall back to the basic way which is what you have mentioned. – Husain Aug 10 '17 at 19:45
  • 1
    @Husain, why don't you just check the state of the entity to see if it's deleted? `var existingEntity = _context.EntityTable.Any(s => s.Code == E.Code && _context.Entry(s).State != EntityState.Deleted);` – johnny 5 Aug 10 '17 at 21:54

2 Answers2

1

Simple call SaveChanges after each operation.

You want to make a transaction when your initializing your context (this makes sure both operation are executed):

_context = new FooEntities();
_transaction = _context.Database.BeginTransaction();

Your Save method will then commit the transaction instead:

_transaction.Commit();

It won't hurt to Dispose the context and the transaction, but I am sure you already do it ;)


On a side note, since you throw a exception anyway:

if (existingEntity != null){
    throw new ArgumentException("Item alread exists.")
}

Why not creating a unique constraint on the Code column? This way the database throws the exception ;)

Christian Gollhardt
  • 16,510
  • 17
  • 74
  • 111
  • Thanks. I am trying to avoid the round trip to DB twice for this. Unique constraint would work for this simplistic example, but not in my case since I have a soft delete. And with the constraint, I will have to decrypt the exception I am getting from the database to give back a useful message to the API user. – Husain Aug 10 '17 at 19:48
  • You can set a uniuqe contraint above multiple columns (e.g Code & IsDeleted). Also Rethrowing a better exception is super easy with C# 6 using [ExceptionFilters](https://stackoverflow.com/a/34813242/2441442). Regarding the roundtrips: Typicaly reading is the expensive one, since this happens far more often than writing ;) @Husain – Christian Gollhardt Aug 10 '17 at 20:05
0

It looks like you just have an order of operations error this might not be the most efficient way but it should take care of your problem.

Item.Delete(E1);
Item.Save();
Item.Insert(E2);
Item.Save();

call them in that order.

or you can add the save method into your delete and insert methods so it saves as it performs each one and you would only have to use two lines.

public void Delete(Entity E) 
{
    var existingEntity = _context.EntityTable.SingleOrDefault(s => s.Code == E.Code);
    _context.EntityTable.Remove(existingEntity);
    Save()
}

public void Insert(Entity E)
{
    var existingEntity = _context.EntityTable.FirstOrDefault(s => s.Code == E.Code);
    if (existingEntity != null){
        throw new ArgumentException("Item alread exists.")
    }

    var newEntity = CreateDbEntity(E); // Create Db Entity just convert the type. Nothing much here.
    _context.EntityTable.Add(newEntity);
    Save()
}

public void Save()
{
    _context.SaveChanges();
}

and then you can call it like

Item.Delete(E1);
Item.Insert(E2);

Hope this helps! If not let me know and I'll remove the answer(I had to use an answer because I can't comment under 50 rep, otherwise I would have used a comment to get some more clarity before answering) Cheers!

Chase
  • 81
  • 1
  • 7
  • Thanks. The problem in general is I want to avoid going to the DB twice. I am using UOW pattern for the Repo so that the business logic objects don't need to go to the DB many times for inserting different objects. – Husain Aug 10 '17 at 19:51