LINQ to SQL (DBML) update entity from different data context - c#

I'm trying to update Entity/model using LINQ to SQL (DBML). but I'm not able to do it.
Here is my code snippet.
public void Update(Customer customer)
{
using (MyDataContext db = new MyDataContext())
{
db.Customers.Attach(customer, true);
db.SubmitChanges();
}
}
public Customer GetByID(int ID)
{
using (MyDataContext db = new MyDataContext())
{
return db.Customers.FirstOrDefault(c => c.CustomerID == ID);
}
}
My scenario is: I get the customer object and bind the customer object to form. and after change the form input data. Data is perfectly change but when I call update method. It's not updating it and I have this error:
An entity can only be attached as modified without original state if it declares a version member or does not have an update check policy.
I searched a lots of internet but I'm not able to find any appropriate solution. I also modified my update function like that:
public void Update(Customer customer)
{
using (MyDataContext db = new MyDataContext())
{
var originalCustomer = db.Customers.FirstOrDefault(c => c.CustomerID == customer.CustomerID);
db.Customers.Attach(customer, originalCustomer);
db.SubmitChanges();
}
}
But still getting the same error.
I don't want to get Customer from database and update it properties from parameter customer properties. I want to use the same parameter customer and update it in database.
Please help me.
Thanks!

The problem is that even though you are using the same DbContext class you are using different DbContext objects because you are using two different "using" statements.
It's important that you use the same DbContext object throughout the request (assuming it's an MVC app).
The usual approach would be to instantiate the DbContext in the controller (or using Dependency Injection ) and use the reference everywhere.

The issue is you have two using statements, giving 2 different context
The easier option would be to inject it into the controller and use it when needed

public void Update(Customer customer)
{
using (MyDataContext db = new MyDataContext())
{
var originalCustomer = db.Customers.Where(c => c.CustomerID == customer.CustomerID).FirstOrDefault();
// change the originalCustomer's properties with customer's properties.
db.SubmitChanges();
}
}

Related

The instance of the entity type cannot be tracked because another instance with the keyvalue is being tracked

I am basically trying to implement CRUD using EntityFrameWork core and .Net core 3.1. I have an issue with my update operation where I am not able update the context with the modified value.
I am using postman to initiate the request.
As you can see in the code below, I am trying to check if that customer exist and if it does pass the modified object to the context.
Function code
[FunctionName("EditCustomer")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous,"post", Route = "update-customer")] HttpRequest req)
{
var customer = JsonConvert.DeserializeObject<CustomerViewModel>(new StreamReader(req.Body).ReadToEnd());
await _repo.UpdateCustomer(customer);
return new OkResult();
}
Repository method
public async Task UpdateCustomer(CustomerViewModel customerViewModel)
{
if (customerViewModel.CustomerId != null)
{
var customer = _context.Customers.Where(c => c.CustomerId.Equals(customerViewModel.CustomerId)).FirstOrDefault();
if (customer == null)
{
throw new Exception("customer not found");
}
else
{
_context.Customers.Update(_mapper.Map<Customers>(customerViewModel));
await _context.SaveChangesAsync();
}
}
}
Mapping
public class CustomerManagerProfile : Profile
{
public CustomerManagerProfile()
{
CreateMap<CustomerDetails, CustomerDetailsViewModel>().ReverseMap();
CreateMap<CustomerOrders, CustomerOrdersViewModel>().ReverseMap();
CreateMap<CustomerOrderDetails, OrderDetailsViewModel>().ReverseMap();
CreateMap<Customers, CustomerViewModel>().ReverseMap();
}
}
Solution
public async Task UpdateCustomer(CustomerViewModel customerViewModel)
{
if (customerViewModel.CustomerId != null)
{
var customer = _context.Customers.Where(c => c.CustomerId.Equals(customerViewModel.CustomerId)).FirstOrDefault();
if (customer == null)
{
throw new Exception("customer not found");
}
else
{
var customerModel = _mapper.Map<Customers>(customerViewModel);
_context.Entry<Customers>(customer).State = EntityState.Detached;
_context.Entry<Customers>(customerModel).State = EntityState.Modified;
await _context.SaveChangesAsync();
}
}
}
Entity Framework tracks your entities for you. For simplicity's sake, think of it like keeping a dictionary (for every table) where the dictionary key is equal to your entity's PK.
The issue is that you can't add two items of the same key in a dictionary, and the same logic applies to EF's change tracker.
Let's look at your repository:
var customer = _context
.Customers
.Where(c => c.CustomerId.Equals(customerViewModel.CustomerId))
.FirstOrDefault();
The fetched customer is retrieved from the database and the change tracker puts it in his dictionary.
var mappedCustomer = _mapper.Map<Customers>(customerViewModel);
_context.Customers.Update();
I split your code in two steps for the sake of my explanation.
It's important to realize that EF can only save changes to tracked objects. So when you call Update, EF executes the following check:
Is this the same (reference-equal) object as one I have I my change tracker?
If yes, then it's already in my change tracker.
If not, then add this object to my change tracker.
In your case, the mappedCustomer is a different object than customer, and therefore EF tries to add mappedCustomer to the change tracker. Since customer is already in there, and customer and mappedCustomer have the same PK value, this creates a conflict.
The exception you see is the outcome of that conflict.
Since you don't need to actually track your original customer object (since EF doesn't do anything with it after fetching it), the shortest solution is to tell EF to not track customer:
var customer = _context
.Customers
.AsNoTracking()
.Where(c => c.CustomerId.Equals(customerViewModel.CustomerId))
.FirstOrDefault();
Since customer is now not put into the change tracker, mappedCustomer won't cause a conflict anymore.
However, you don't actually need to fetch this customer at all. You're only interested in knowing whether it exists. So instead of letting EF fetch the entire customer object, we can do this:
bool customerExists = _context
.Customers
.Any(c => c.CustomerId.Equals(customerViewModel.CustomerId));
This also solves the issue since you never fetch the original customer, so it never gets tracked. It also saves you a bit of bandwidth in the process. It's admittedly negligible by itself, but if you repeat this improvement across your codebase, it may become more significent.
The most simple adjustment that you could make would be to avoid tracking your Customers on retrieval like this:
var customer = _context
.Customers
.AsNoTracking() // This method tells EF not to track results of the query.
.Where(c => c.CustomerId.Equals(customerViewModel.CustomerId))
.FirstOrDefault();
It's not entirely clear from the code, but my guess is your mapper returns a new instance of Customer with the same ID, which confuses EF. If you would instead modify that same instance, your call to .Update() should work as well:
var customer = _context.Customers.Where(c => c.CustomerId.Equals(customerViewModel.CustomerId)).FirstOrDefault();
customer.Name = "UpdatedName"; // An example.
_context.Customers.Update(customer);
await _context.SaveChangesAsync();
As a matter of fact, if you track your Customer you don't even need to explicitly call .Update() method, the purpose of tracking is to be aware of what changes were made to the entities and should be saved to the database. Therefore this will also work:
// Customer is being tracked by default.
var customer = _context.Customers.Where(c => c.CustomerId.Equals(customerViewModel.CustomerId)).FirstOrDefault();
customer.Name = "UpdatedName"; // An example.
await _context.SaveChangesAsync();
EDIT:
The solution you yourself provide begins by tracking the results of your query (the Customer) instance, then stops tracking it (a.k.a. gets detached) before writing to database and instead starts tracking the instance that represents the updated Customer and also marks it as modified. Obviously that works as well, but is just a less efficient and elegant way of doing so.
As a matter of fact if you use this bizarre approach, I don't see the reason for fetching your Customer at all. Surely you could just:
if (!(await _context.Customers.AnyAsync(c => c.CustomerId == customerViewModel.CustomerId)))
{
throw new Exception("customer not found");
}
var customerModel = _mapper.Map<Customers>(customerViewModel);
_context.Customers.Update(customerModel);
await _context.SaveChangesAsync();
You use AutoMapper wrong way. It is not created to map from View model or DTO to Entity classes. It makes many problems and you are facing with only one of them now.
If you have more complex bussiness logic in you app (not just udpate all fields), it will be horrible to manage, test and debug what actually is happening in your code. You should write you own logic with some bussiness validation in case when you want to make some other update than CRUD.
If I were you I would create UpdateFields method in Customer class which would update them and finally call SaveChanges. It depends on whether you use anemic entity (anti)pattern or not. If you do not want your entity class to have any method you can create just method which manually map you VM do entity with some domain validation

Entity Framework Navigation Properties not loading til new DbContext is established

I'm still relatively new to EF, so forgive me if I'm missing an obvious concept.
Let me see if I can simplify this...Old question is in edit history, but I think I can distill this down:
FWIW, DbContext is PER REQUEST, not static, which is why the first example works. Not using DI/IoC on the controller at this point.
public class OrdersController : ApiController {
private MyDbContext db = new MyDbContext();
//controller methods....
protected override void Dispose(bool disposing) {
db.Dispose();
}
}
Works (2 separate requests):
//request 1: client code sends in a new order to be added to db
public Order Post([FromBody]Order order) {
db.Orders.Add(order);
db.SaveChanges();
return order;
}
//request 2: someone punches a button to send an email to CS about this order
public void NotifyCustomerService(int orderid) {
var order = db.Orders.Find(orderid);
//do email code here
}
Broken (single request):
//request: client code sends in a new order to be added to db AND notifies CS at same time
public Order Post([FromBody]Order order) {
db.Orders.Add(order);
db.SaveChanges();
//notify CS via email here (nav properties are not populating)
return order;
}
Works (single request) (but i know this is horrible practice):
//request: client code sends in a new order to be added to db AND notifies CS at same time (using a new dbcontext in the notification code)
public Order Post([FromBody]Order order) {
db.Orders.Add(order);
db.SaveChanges();
using(var db2 = new MyDbContext()) {
var sameOrderWTF = db.Orders.Find(order.ID);
//notify CS via email using sameOrderWTF instance here (nav properties ARE populating)
}
return order;
}
So, it seems to me, that there's some side effect of adding a new never-before-seen entity to the context, and then trying to get it's nav properties to populate. But if you create a new DbContext... even in the same request, it has to directly hit the DB for that entity, not use the in-mem copy, so then the nav properties magically work. That's the part that has me stumped.
Working Solution
//request: client code sends in a new order to be added to db AND notifies CS at same time
public Order Post([FromBody]Order o) {
Order order = db.Orders.Create();
db.Orders.Add(order);
//copy values from input to proxy instance
db.Entry(order).CurrentValues.SetValues(o);
//copy input lines to proxy instance (same process as order for each line)
o.OrderLines.ToList().ForEach(l => {
var line = db.OrderLines.Create();
db.OrderLines.Add(line);
db.Entry(line).CurrentValues.SetValues(l);
order.OrderLines.Add(line);
});
db.SaveChanges();
//notify CS via email here (nav properties are not populating)
return order;
}
So while we'll consider this question answered (thanks Uber Bot), the need to go through all of that seems more laborious than my other (admittedly short) experience with ASP.NET MVC and EF. I guess maybe I should be using ViewModels and mapping the VM properties to a proxy instance instead of trying to use the EF classes directly, but I just can't really see the benefit for a simple call like this.
Your new Order entity instance is not wrapped by proxy and so lazy loading will not work.
You can force context to load navigational property.
db.Entry(order).Reference(o => o.YouProperty).Load();
Or you can create an instance by using context's factory to overcome this problem.
db.Orders.Create();
A proxy instance will not be created if you create an instance of an
entity using the new operator. This may not be a problem, but if you
need to create a proxy instance (for example, so that lazy loading or
proxy change tracking will work) then you can do so using the Create
method of DbSet.
https://msdn.microsoft.com/en-us/data/jj592886.aspx
Just an FYI... could be simplified like..... you should add validation.
//only handles new orders...as is
//Assumes!!! Order is of a type which is a db Entity! (Table)
//Assumes that you populated "order" with all the required properties.
public Order Post([FromBody]Order order)
{
db.Orders.Add(order);
db.SaveChanges();
return order;
}
Checking your code.... again it seems like "Order" in the Method signature is not a db Order entity, not sure tho if it isn't you should rename it so that it's not confusing. I.e. rename it as OrderDTO or something else which is not the same name as the db Order. It will make understanding of your code much clearer.

Entity Framework Single line CRUD

Seeking advice for best practice
Very often I have to do the CRUD operation for a single record in my ASP.NET application.
For deletion, I was doing like
var myRecord = context.myTable.Find(myID);
if (myRecord != null)
{
context.myTable.Remove(myRecord);
context.SaveChanges();
}
Thinking about doing like
new context().myTable.Find(myID).Remove();
After reading some EF and repository pattern staff (especially Here ), I understand with the help of generic repository, at least I can do:
class UnitOfWork
{
void RemoveRecord(myRecord)
{
var context = FindMyContext(myRecord); //[here](http://blogs.msdn.com/b/alexj/archive/2009/06/08/tip-24-how-to-get-the-objectcontext-from-an-entity.aspx)
context.Entry(myRecord).State = EntityState.Deleted;
context.SaveChanges();
}
}
And I can use it like in my code:
new UnitOfWork.RemoveRecord(myRecord);
Is this a good idea?
First ask yourself what would be the added value of the repository and unit of work pattern on top of entity framework?
The context of Entity Framework already is a unit of work and already is a repository, so why create yet another one?
Also, you should create a context from the moment you need it and dispose it as soon as possible. Your service operation could look like:
public void RemoveCustomer(int customerId)
{
using(var context = new MyDbContext())
{
var customer = context.Customers.SingleOrDefault(c => c.Id == customerId);
if (customer==null) throw new BusinessException("Customer does not exist");
context.Customers.Remove(customer);
context.SaveChanges();
}
}
Personally I inject this context with a IoC framework like using StructureMap.

Controller method not updating(same result every time)

I have following method in my mvc controller:
[HttpGet]
public ActionResult UserProfile(String username)
{
var user = db.Users.Find(username);
return View(user);
}
This function returns View with user profile. But result of this is the same, regardless of changes in database.
When I debug it seems like db is not changing at all, while in other controllers everything works just fine.
EDIT:
Place when I make changes
public ActionResult ExecuteRetreive(String username, String ISBN)
{
if (IsValid(username))
{
var resBook = db.Books.Find(ISBN);
var resUser = db.Users.Find(username);
var resRentedBooks = (from rb in db.RentedBooks
join b in db.Books on rb.ISBN equals b.ISBN
where b.ISBN == ISBN
where rb.Login == username
where rb.Returned == null
select rb).FirstOrDefault();
if (resRentedBooks == null)
{
return RedirectToAction("Fail", "FailSuccess",
new { error = "" });
}
resRentedBooks.Returned = DateTime.Now;
resBook.IsRented = false;
resUser.RentedBooks--;
db.SaveChanges();
return RedirectToAction("Success", "FailSuccess");
}
else
{
return RedirectToAction("Fail", "FailSuccess",
new { error = "Niepoprawna nazwa użytkownika" });
}
}
Im new to this so dont laugh at my code :P When I display resUser.RentedBooks--; it is the same every time.
As a follow up to what #JeroenVannevel said in the comments, another problem that you might be having because you're using a static context (and one that I've had to deal with in the past) is that once a specific DbContext has loaded an entity (or a set of entities, in my case) it won't tend to refresh just because some outside changes were made in the database. It loads those entities into Local and just refers to those automatically if you query for it.
The solution, then, is to always put your DbContext calls wrapped up in a using block, since DbContext implements IDisposable.
One word of caution with this approach, since you're using MVC: If you are using lazy loading, and you know that your View will need some information from a child object (or to list the names of a collection of child objects), you will absolutely need to hydrate those child entities before you get out of the using block, or you will find yourself getting exceptions saying that your context has been disposed.

Declaring Entity FrameWork Contexts with using

Which is the Best Practise in Declaring Entity FrameWork Contexts
function()
{
DBContext context = new DBContext();
//Entity code
return ;
}
or
function()
{
using(DBContext context = new DBContext())
{
//Entity code
}
}
Do we need to use using in EntityFrameWork ? If yes my 2nd question
In DataAccess Layer am executing EF and storing the result in IEnumerable inside using
MY DL
function()
{
IEnumerable something = null;
using(DBContext context = new DBContext())
{
IEnumerable something = ....
}
return something;
}
In Controller
function()
{
List some = something.ToList();
}
And in my controller am getting this as a list as i need to do some Find operation am getting
"The operation cannot be completed because the DbContext has been disposed Entity Framework"
Yes i can return a list from DL and it works fine
How do i handle this if i use using with IEnumerable?
You can avoid the lazy-loading EF behaviour by calling .ToList() on the IEnumerable before the context is disposed (i.e. within your using block)
Yes, a using is the best practice because it cleans up your context. The Using statement is a shortcut for:
try {
// Execute your code inside the using statement
}
finally {
// Cleanup the context no matter what by calling .Dispose()
}
Keep in mind, your context likely returns IEnumerables and since EF supports lazy loading these objects won't be populated until you fetch them to a concrete collection (ie. yourResult.ToList()).
A common negative outcome occurs in this scenario:
public IEnumerable<Employee> GetEmployeesInAccounting()
{
using(var myContext = new MyDbContext())
{
return myContext.Employees.Where(emp => emp.Department == 'Accounting');
}
}
// Code that fails, Assuming Manager is a lazy loaded entity, this results in an exception but it compiles no problem
var acctEmps = GetEmployeesInAccounting();
var something = acctEmps.First().Department.Manager.Department;
You can avoid this by using the .Include(emp => emp.Manager) (linq extension method) and binding your result using .ToList();
Your request will be executed toward the datasource as soon as you'll call .ToList() method.
That's why you cannot perform .ToList() in your Controller as your context as been disposed at the end of the using block.
In your DL method, just do something like:
IEnumerable<Something> function()
{
using(DBContext context = new DBContext())
{
return something.ToList();
}
}
and in your Controller you'll get an IEnumerable of Something:
var mySomethingIEnumerable = DL.Function();
Hope that helps!

Categories