Linq-To-Sql SubmitChanges Not Updating Database - asp.net

I've read multiple questions similar to this one but none are exactly my situation.
Using linq-to-sql I insert a new record and submit changes. Then, in the same web request, I pull that same record, and update it, then submit changes. The changes are not saved. The DatabaseContext is the same across both these operations.
Insert:
var transaction = _factory.CreateTransaction(siteId, userId, questionId, type, amount, transactionId, processor);
using (IUnitOfWork unitOfWork = UnitOfWork.Begin())
{
transaction.Amount = amount;
_transactionRepository.Add(transaction);
unitOfWork.Commit();
}
Select and Update:
ITransaction transaction = _transactionRepository.FindById(transactionId);
if (transaction == null) throw new Exception(Constants.ErrorCannotFindTransactionWithId.FormatWith(transactionId));
using (IUnitOfWork unitOfWork = UnitOfWork.Begin())
{
transaction.CrmId = crmId;
transaction.UpdatedAt = SystemTime.Now();
unitOfWork.Commit();
}
Here's the unit of work code:
public virtual void Commit()
{
if (_isDisposed)
{
throw new ObjectDisposedException(GetType().Name);
}
_database.SubmitChanges();
}
I even went into the designer.cs file and put a breakpoint on the field that is being set but not updated. I stepped through and it entered and execute the set code, so the Entity should be getting "notified" of the change to this field:
public string CrmId
{
get
{
return this._CrmId;
}
set
{
if ((this._CrmId != value))
{
this.OnCrmIdChanging(value);
this.SendPropertyChanging();
this._CrmId = value;
this.SendPropertyChanged("CrmId");
this.OnCrmIdChanged();
}
}
}
Other useful information:
ObjectTracking is enabled
No errors or exceptions when second SubmitChanges is called (just silently fails update)
SQL profiler shows insert and select but not the subsequent update statement. Linq-To-Sql is not generating the update statement.
There is only one database, one database string, so the update is not going to another database
The table has a primary key.
I don't know what would cause Linq-To-Sql to not issue the update command and not raise some kind of error. Perhaps the problem stems from using the same DataContext instance? I've even refreshed the object from the database using the DataContact.Refresh method before it is pulled for the update, but that didn't help.

I have found what is likely to be the root cause. I am using Unity. The initial insert is being performed in a service class with a PerWebRequest lifetime. The select and update is happening in a class with a Singleton lifetime. So my assumption that the DataContext instances are the same was incorrect.
So, in my class with the Singleton lifetime, I get a fresh instance of the database repository and perform the update and no problem.
Now I still don't know why the original code didn't work and my approach could still be considered more a workaround than a solution, but it did solve my problem and hopefully will be useful to others.

Related

EF Core Update with List

To make updates to a record of SQL Server using Entity Framework Core, I query the record I need to update, make changes to the object and then call .SaveChanges(). This works nice and clean.
For example:
var emp = _context.Employee.FirstOrDefault(item => item.IdEmployee == Data.IdEmployee);
emp.IdPosition = Data.IdPosition;
await _context.SaveChangesAsync();
But is there a standard method if I want to update multiple records?
My first approach was using a list passing it to the controller, but then I would need to go through that list and save changes every time, never really finished this option as I regarded it as not optimal.
For now what I do is instead of passing a list to the controller, I pass each object to the controller using a for. (kind of the same...)
for(int i = 0; i < ObjectList.Count; i ++)
{
/* Some code */
var httpResponseObject = await MyRepositories.Post<Object>(url+"/Controller", Object);
}
And then do the same thing on the controller as before, when updating only one record, for each of the records...
I don't feel this is the best possible approach, but I haven't found another way, yet.
What would be the optimal way of doing this?
Your question has nothing to do with Blazor... However, I'm not sure I understand what is the issue. When you call the SaveChangesAsync method, all changes in your context are committed to the database. You don't have to pass one object at a time...You can pass a list of objects
Hope this helps...
Updating records in bulk using Entity Framework or other Object Relational Mapping (ORM) libraries is a common challenge because they will run an UPDATE command for every record. You could try using Entity Framework Plus, which is an extension to do bulk updates.
If updating multiple records with a single call is critical for you, I would recommend just writing a stored procedure and call if from your service. Entity Framework can also run direct queries and stored procedures.
It looks like the user makes some changes and then a save action needs to persist multiple records at the same time. You could trigger multiple AJAX calls—or, if you need, just one.
What I would do is create an endpoint—with an API controller and an action—that's specific to your needs. For example, to update the position of records in a table:
Controller:
/DataOrder
Action:
[HttpPut]
public async void Update([FromBody] DataChanges changes)
{
foreach(var change in changes)
{
var dbRecord = _context.Employees.Find(change.RecordId);
dbRecord.IdPosition = change.Position;
}
_context.SaveChanges();
}
public class DataChanges
{
public List<DataChange> Items {get;set;}
public DataChangesWrapper()
{
Items = new List<DataChange>();
}
}
public class DataChange
{
public int RecordId {get;set;}
public int Position {get;set;}
}
The foreach statement will execute an UPDATE for every record. If you want a single database call, however, you can write a SQL query or have a stored procedure in the database and pass the data as a DataTable parameter instead.

EF 5.0 Trouble updating entity which is already tracked

I'll preface this question with the following: I know there are a million posts on the internet about the old "An object with the same key already exists in the ObjectStateManager" issue. My scenario is a bit more complicated, I think.
I have a UnitOfWork class which creates a DbContext and passes it to any repository which is called. The pattern I'm using closely follows the Unit of Work tutorial on the ASP.NET site. Unlike the tutorial, my repositories take in Business entities, map them to data entities, and perform some CRUD action. My Business logic only works with Business entities. Here is what I'm trying to do in a sample Business Manager class:
_unitOfWork.Repository.Add(entity);
_unitOfWork.Save(); // context.SaveChanges() under the hood
...Perform some operations on the model...
_unitOfWork.Repository.Update(entity);
_unitOfWork.Save();
Here is a sample Update method from the repository:
public virtual void Update(entity)
{
var dataEntity = // map from business entity to data;
_context.Entry(dataEntity).State = EntityState.Modified;
}
It obviously fails on the last line. Here is where my confusion sets in:
The entity's State is Detached
When I attempt to change the State to Modified or Unchanged, it gives me the ObjectStateManager exception above.
When I attempt to detach the entity from the context (((IObjectContextAdapter)_context).ObjectContext.Detach(entity);) I get an exception about how the entity is not attached to the context, therefore, it cannot detach it. Very confusing (something fundamental I'm missing, for sure).
Many other posts suggest I make a database call, update that entity in the repository, then _unitOfWork.Save(). I don't like this approach. I shouldn't need to make an unnecessary network call to update an entity.
The Update method in the repository needs to handle two scenarios: 1) updating an entity which is not currently tracked by the context, and 2) updating an entity which IS currently tracked by the context. The second piece is what I'm struggling with.
Any help or insight is appreciated.
Thanks!
This means that there already is an object attached to the context with the same key as the new dataEntity. The existing object and the new entity both represent the same entry in the database but they are two different objects.
This may indicate that the lifespan of your _context is too long, but that's hard to judge from your code. It is certain though that the context was previously used to fetch an entity from the database that is subsequently duplicated by var dataEntity = ....
You may have to shorten the lifespan of the context, I can't tell. If you think it's OK you may want to use the Local collection to check whether the entity is already there. That will save the database round trip that Find may still make.
I found a hybrid solution which appears to work:
public virtual void Update(TB entity)
{
var dataEntity = Mapper.Map<TB, TD>(entity);
var pkey = _dbSet.Create().GetType().GetProperty("Id").GetValue(dataEntity);
var entry = _context.Entry(dataEntity);
if (entry.State == EntityState.Detached)
{
var attachedEntity = _dbSet.Find(pkey);
if (attachedEntity != null)
{
var attachedEntry = _context.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(dataEntity);
}
else
{
entry.State = EntityState.Modified;
}
}
else
{
entry.State = EntityState.Modified;
}
}

Dispose DbContext not dispose the ObjectContext entities

I'm using EF 5.0 to create a web and I have some issues disposing my context. All the times that I use a context is inside a using sentence, so the context should be disposed automatically but in a specific moment I get the next error when I try to attach an entity to a context:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
It semms that the entity is not disposed. How is the way to manage this situation? Do I have to dispose the ObjectContext to dispose the entities or is there any way to check if the Entity is attached?
Regards.
One way to do it is to detach the existing object before attaching. I don't have VS in front of me so I apologize if the code isn't exactly correct.
var existingObject = dbContext.Users.Local
.FirstOrDefault(x => x.id = newObject.id);
if (existingObject != null)
{
// remove object from local cache
dbContext.Entry(existingObject).State = EntityState.Detached;
}
dbContext.Users.Attach(newObject);
In case this doesn't fix the problem, you'll have to go to the old way of detaching objects.
// remove object from local cache
ObjectContext objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
objectContext.Detach(existingObject);
If you do something like that:
User u;
using (Entities ent = new Entities())
{
u = ent.Users.Single(a => a.ID == 123);
}
using (Entities ent2 = new Entities())
{
//loading the same user
User user2 = ent2.Users.Single(a => a.ID == 123);
//trying to attach the same object with the same key
ent2.Attach(u);
}
then you will get this error (I haven't tested this code).
EDIT: one of the solutions is to change the object's state:
ent2.Attach(u);
ent2.ObjectStateManager.ChangeObjectState(u, EntityState.Modified);
another solution is to check if the entity is already attached:
ObjectStateEntry state = null;
if(!ent2.ObjectStateManager.TryGetObjectStateEntry(((IEntityWithKey)u).EntityKey, out state))
{
ent2.Attach(u);
}
Dispose doesn't mean "reset to factory settings". It is a way to clean up unmanaged resources like database connections and such.
The problem has nothing to do with disposing a context or not. It even has nothing to do with having multiple contexts somewhere in place. If this would be the problem you would get the "An entity object cannot be referenced by multiple instances of IEntityChangeTracker" exception which is totally different to your exception.
You can simulate your exception quite easily with only a single context:
using (var ctx = new MyContext())
{
var customer1 = new Customer { Id = 1 };
var customer2 = new Customer { Id = 1 }; // a second object with the same key
ctx.Customer.Attach(customer1);
ctx.Customer.Attach(customer2); // your exception will occur here
}
The problem causing this exception is normally more hidden, expecially if you keep in mind that attaching or setting a state (for example to Modified) will also attach all related entities in the object graph of the entity you are attaching. If in this graph are two objects with the same key you'll get the exception as well, although you didn't attach those related entities explicitly.
But it's impossible to find the exact reason without more details about your code.

nhibernate transactions and unit testing

I've got a piece of code that looks like this:
public void Foo(int userId)
{
try {
using (var tran = NHibernateSession.Current.BeginTransaction())
{
var user = _userRepository.Get(userId);
user.Address = "some new fake user address";
_userRepository.Save(user);
Validate();
tran.Commit();
}
}
catch (Exception) {
logger.Error("log error and don't throw")
}
}
private void Validate()
{
throw new Exception();
}
And I'd like to unit test if validations ware made correctly. I use nunit and and SQLLite database for testing. Here is test code:
protected override void When()
{
base.When();
ownerOfFooMethod.Foo(1);
Session.Flush();
Session.Clear();
}
[Test]
public void FooTest()
{
var fakeUser = userRepository.GetUserById(1);
fakeUser.Address.ShouldNotEqual("some new fake user address");
}
My test fails.
While I'm debugging I can see that exception is thrown, Commit has not been called. But my user still has "some new fake user address" in Address property, although I was expecting that it will be rollbacked.
While I'm looking in nhibernate profiler I can see begin transaction statement, but it is not followed neither by commit nor by rollback.
What is more, even if I put there try-catch block and do Rollback explicitly in catch, my test still fails.
I assume, that there is some problem in testing environment, but everything seems fine for me.
Any ideas?
EDIT: I've added important try-catch block (at the beginning I've simplified code too much).
If the exception occurs before NH has flushed the change to the database, and if you then keep using that session without evicting/clearing the object and a flush occurs later for some reason, the change will still be persisted, since the object is still dirty according to NHibernate. When rolling back a transaction you should immediately close the session to avoid this kind of problem.
Another way to put it: A rollback will not rollback in-memory changes you've made to persistent entities.
Also, if the session is a regular session, that call to Save() isn't needed, since the instance is already tracked by NH.

Updating object with related entities from detached state

When I query from the entity framework I always query in a detached state so that the records retrieved can be stored in cache for subsequent requests.
Right now I have a form that the user can edit which contains a parent record, and then two lists of parent records.
When the data is POSTed to the server, I take my view models and map them into the entity framework objects using AutoMapper. The data looks fine; AutoMapper is mapping the data correctly.
When I attach the object so that I can update it, an exception is thrown: A referential integrity constraint violation occurred: The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship.
public static void UpdateOrder(ShippingOrder shippingOrder) {
using (OrderEntity orderContext = new OrderEntity()) {
//Exception happens here
orderContext.ShippingOrders.Attach(shippingOrder);
//Update the order itself; mark the order has being modified so the EF will update it.
orderContext.ObjectStateManager.ChangeObjectState(shippingOrder, System.Data.EntityState.Modified);
//Perform the update.
orderContext.SaveChanges();
}
}
The EntityFramework (EF) seems to think that my keys aren't lining up, but I'm not sure what isn't correct. The foreign key property does have the correct value, so I'm not sure what it's checking. Does anyone have any ideas?
You might try something like this:
ShippingOrder existingShippingOrder = orderContext.ShippingOrders.Find(shippingOrder.ID);
orderContext.Entry(existingShippingOrder ).CurrentValues.SetValues(shippingOrder);
Instead of
orderContext.ObjectStateManager.ChangeObjectState(shippingOrder, System.Data.EntityState.Modified);
try this
orderContext.Entry(ShippingOrder).State = EntityState.Modified;
as explained here
Insert or update pattern A common pattern for some applications is to
either Add an entity as new (resulting in a database insert) or Attach
an entity as existing and mark it as modified (resulting in a database
update) depending on the value of the primary key. For example, when
using database generated integer primary keys it is common to treat an
entity with a zero key as new and an entity with a non-zero key as
existing. This pattern can be achieved by setting the entity state
based on a check of the primary key value. For example:
public void InsertOrUpdate(DbContext context, Unicorn unicorn)
{
context.Entry(unicorn).State = unicorn.Id == 0 ?
EntityState.Added :
EntityState.Modified;
context.SaveChanges();
}
you can try
public static void UpdateOrder(ShippingOrder shippingOrder) {
using (OrderEntity orderContext = new OrderEntity()) {
orderContext.Entry(shippingOrder).State = shippingOrder.Id==0?
EntityState.Added :
EntityState.Modified;
orderContext.SaveChanges();
}
}
UPDATE:
for ObjectContext class you can try
public static void UpdateOrder(ShippingOrder shippingOrder) {
using (OrderEntity orderContext = new OrderEntity()) {
orderContext.ObjectStateManager.ChangeObjectState(shippingOrder, EntityState.Modified);
orderContext.SaveChanges();
}
}

Resources