Entity-framework doesn't save changes to database - entity-framework

I have a problem with saving changes to the database with entity framework. I'm using three tables; AspNetUsers, tblCountry, and tblCountry_AspNetUsers.
tblCountry_AspNetUsers consists of two columns; UserId and CountryId, which creates a one-to-many relationship between tblCountry and AspNetUsers.
Currently, what I want to do is change the country of a specific user, but entity framework doesn't let me access the tblCountry_AspNetUsers database, and instead creates an ICollection of AspNetUsers on tblCountry. I could assign the Id on the AspNetUser directly, but I don't want to start adding/removing columns from identity tables just yet since I'm using database first, and I've heard it can lead to problems.
Anyway, I can remove just fine from the ICollection and save those changes to the database, but when I try to add the same user to a different country, it doesn't save to the database properly, but I can find the user in the context object when debugging.
I've tried attaching and changing the entitystate to both added and modified, but when I try to do this, it breaks out of the method and doesn't update the database. (basically it freezes when I try to attach)
My code for editing a user looks like the following:
(Note that UserManager handles the identity usermodel, ApplicationUser,, which is not the same as AspNetUsers in this aspect)
(Also, tblCountry.AspNetUsers refers to the ICollection of users assigned to a specific country)
...
var aspuser = new AspNetUsers();
using (DbContext dc = new DbContext())
{
aspuser = dc.AspNetUsers.First(x => x.Id == userid);
var user = await UserManager.FindByIdAsync(userid);
user.Email = updatedUser.UserName;
user.UserName = updatedUser.Email;
var result = await UserManager.UpdateAsync(user);
aspuser.tblCountry.AspNetUsers.Remove(aspuser);
await dc.SaveChangesAsync();
}
using (DbContext dc = new DbContext())
{
var c = _country.GetById(newcountryid);
c.AspNetUsers.Add(aspuser);
await dc.SaveChangesAsync();
}
return Users(userid);
}
It would be extremely easy if it was a table I could access directly, but with an ICollection like this I'm confused as to what I should do to make it work, and I appreciate any input!
Cheers

This line aspuser = dc.AspNetUsers.First(x => x.Id == userid); is inside using (DbContext dc = new DbContext()) which means that object aspuser is detached from context when you leave that block. You should do:
var c = _country.GetById(newcountryid);
c.AspNetUsers.Add(aspuser);
await dc.SaveChangesAsync();
in the same using block as the above code, why did you separate it?

Related

Auto detection of changes with disconnected entities

I am making a simple editor on a web server that lets user change/add data to a single table stored on a MS SQL server.
I am using Entity Framework 6 to do this, and I am wondering how I should do to track the changes made to the entity model.
I would have hoped that I could load new data in the context, and have the context automatically diff against what's in the DB, and then call SaveChanges().
But from what I read online, it looks like I need to loop through all the data, and check myself what changed, so that I can then call Context.Entry(myEntry).State = Added or Context.Entry(myEntry).State = Modified
Is there no way for EF to automatically detect what's new, what's modified and what's unchanged?
I would recommend passing ViewModels or DTOs to the view, then map them back to the reloaded entity on a commit. EF will automatically only update values that change when setting values. Setting a value without changing the value will not trigger an update. (Where attaching an entity, and setting it's modified state will update all columns) Passing entities, while convenient, exposes more about your data structure than your UI may present, and can be tampered with before being sent back. Never trust anything coming back from the client. When serialized to a client, the data is no longer an entity, it is a JSON block of data. When sent back to the server, it isn't a tracked entity, it is a POCO with the entity's signature. No change tracking that EF entities can provide will apply on the client or survive serialization/deserialization.
For example:
Given a Child that has a name and birth date. We select a DTO to pass to the view. The view changes a name, we get the DTO back and copy all values, modified or otherwise back into the entity and call SaveChanges()
// For example, loading the child in the controller to pass to the view...
ChildDTO childDto = null;
using (var context = new TestDbContext())
{
childDto = context.Children
.Select(x => new ChildDto
{
ChildId = x.ChildId,
Name = x.Name,
BirthDte = x.BirthDate
}).Single(x => x.ChildId == 1);
}
// View updates just the name...
childDto.Name = "Luke";
// Example if the view passed DTO back to controller to update...
using (var context = new TestDbContext())
{
var child = context.Children.Single(x => x.ChildId == 1);
child.Name = childDto.Name;
child.BirthDate = childDto.BirthDate;
context.SaveChanges();
}
If the name changed and the birth date did not, the EF generated update statement would only update the Name. If the entity name was already "Luke", then no Update statement would be issued. You can verify this behavior with an SQL profiler to see if/when/what SQL EF sends to the database.
Automapper can help simplify this for getting the DTO back into the entity:
var mappingConfig = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Child, ChildDTO>();
cfg.CreateMap<ChildDTO, Child>();
});
Then when reading, leverage ProjectTo instead of Select:
using (var context = new TestDbContext())
{
childDto = context.Children
.ProjectTo<ChildDTO>(mappingConfig)
.Single(x => x.ChildId == 1);
}
... and when updating the entity:
using (var context = new TestDbContext())
{
var child = context.Children.Single(x => x.ChildId == 1);
var mapper = mappingConfig.CreateMapper();
mapper.Map(childDto, child); // copies values from DTO to the entity instance.
context.SaveChanges();
}
It's important to validate the DTO prior to copying values across to the Entity, whether doing it manually or with Automapper. Automapper config can also be set up to only copy over values that are expected/allowed to change.

How I can refresh context in Entity Framework?

I am using Entity Framework code-first and I call edit user, inside edit user I call a stored procedure that is editing the user. After that, I call another method to get the user from the database.
The problem is that this method returns the old user so I think I need to refresh the context before calling the second method. How I can refresh the context in Entity Framework?
Based on this page:
Entity Framework Refresh context?
I try to use this:
yourContext.Entry(yourEntity).Reload();
but I'm not sure how to do this exactly.
This is my context:
var newUser = await authenticationContext.Users.Where(u => u.Email == "user1#test.com").FirstOrDefaultAsync();
I tried:
authenticationContext.Entry(authenticationContext.Users).Reload();
but it is throwing an error.
System.InvalidOperationException: the entity type Dbset is not part of the model for the current context.
Have you tried reinitializing the context?
var newUser = await authenticationContext.Users.Where(u => u.Email == "user1#test.com").FirstOrDefaultAsync();
// do cool stuff with sprocs and whatnot here
authenticationContext = new AuthenticationContext(); // Or whatever your context model looks like
var newUser = await authenticationContext.Users.Where(u => u.Email == "user1#test.com").FirstOrDefaultAsync();
Or simply by using "usings" where you want always fresh data, instead of keeping the context as a field?
using (var authenticationContext = new AuthenticationContext())
{
var newUser = await authenticationContext.Users.Where(u => u.Email == "user1#test.com").FirstOrDefaultAsync();
}
The method you've tried is for reloading a single entity that exists in EF's tracker. You can't supply a DbSet to that method. I suppose you would use that method if you had an entity attached to the tracker:
var newUser = await authenticationContext.Users.Where(u => u.Email == "user1#test.com").FirstOrDefaultAsync();
// do cool stuff with sprocs and whatnot here
authenticationContext.Entry(newUser).Reload();
As mentioned in that answer though, you might need to think about navigation props. Also you need to keep in mind that if you change that value of what EF is using to track entities (I suppose it's the primary key, but I'm not sure), you might get unwanted results.
So that's what you can do with code. But you should clearly consider what's suggested in the comments, having to fetch data you should already have indicates code smell. Also I haven't tested any of this, hopefully it works and/or you get the gist of it.

Entity Framework 6: Data Manipulation

I'm new to EF and I've only worked with EF 6. When I use it to access the data from a database that I have already designed I don't see any method that allows me to manipulate data. To solve this temporarily I created some stored procedures for adding, deleting and updating data.
I would like to know if what I am doing is the right way to manipulate data in EF or not. In case it is not the right way how can I do this using the built in features of EF6. MSDN said there is an add object but couldn't find it.
There have been some changes in the API. EF6 does not use ObjectContext anymore, it uses a DbContext. This can be generated from a Database Model, or created using a Model first approach.
Old syntax:
objectContext.AddToUsers(user);
is now:
dbContext.Users.Add(user);
Here are some basic samples:
insert:
using(var dbContext = new MyDbContext())
{
var user = new User { ID=1, Name="Test" };
dbContext.Users.Add(user); // Add user
dbContext.SaveChanges(); // Save changes to DB
}
update:
using(var dbContext = new MyDbContext())
{
var user = dbContext.Users.Find(1);// find by ID = 1
user.Name = "New Name"; // Change name
dbContext.SaveChanges(); // Save changes to DB
}
delete:
using(var dbContext = new MyDbContext())
{
var user = dbContext.Users.Find(1);// find by ID = 1
dbContext.Users.Remove(user); // delete user
dbContext.SaveChanges(); // Save changes to DB
}
So, no need for stored procedures.. definetly not needed for simple CRUD.

Having a hard time with Entity Framework detached POCO objects

I want to use EF DbContext/POCO entities in a detached manner, i.e. retrieve a hierarchy of entities from my business tier, make some changes, then send the entire hierarchy back to the business tier to persist back to the database. Each BLL call uses a different instance of the DbContext. To test this I wrote some code to simulate such an environment.
First I retrieve a Customer plus related Orders and OrderLines:-
Customer customer;
using (var context = new TestContext())
{
customer = context.Customers.Include("Orders.OrderLines").SingleOrDefault(o => o.Id == 1);
}
Next I add a new Order with two OrderLines:-
var newOrder = new Order { OrderDate = DateTime.Now, OrderDescription = "Test" };
newOrder.OrderLines.Add(new OrderLine { ProductName = "foo", Order = newOrder, OrderId = newOrder.Id });
newOrder.OrderLines.Add(new OrderLine { ProductName = "bar", Order = newOrder, OrderId = newOrder.Id });
customer.Orders.Add(newOrder);
newOrder.Customer = customer;
newOrder.CustomerId = customer.Id;
Finally I persist the changes (using a new context):-
using (var context = new TestContext())
{
context.Customers.Attach(customer);
context.SaveChanges();
}
I realise this last part is incomplete, as no doubt I'll need to change the state of the new entities before calling SaveChanges(). Do I Add or Attach the customer? Which entities states will I have to change?
Before I can get to this stage, running the above code throws an Exception:
An object with the same key already exists in the ObjectStateManager.
It seems to stem from not explicitly setting the ID of the two OrderLine entities, so both default to 0. I thought it was fine to do this as EF would handle things automatically. Am I doing something wrong?
Also, working in this "detached" manner, there seems to be an lot of work required to set up the relationships - I have to add the new order entity to the customer.Orders collection, set the new order's Customer property, and its CustomerId property. Is this the correct approach or is there a simpler way?
Would I be better off looking at self-tracking entities? I'd read somewhere that they are being deprecated, or at least being discouraged in favour of POCOs.
You basically have 2 options:
A) Optimistic.
You can proceed pretty close to the way you're proceeding now, and just attach everything as Modified and hope. The code you're looking for instead of .Attach() is:
context.Entry(customer).State = EntityState.Modified;
Definitely not intuitive. This weird looking call attaches the detached (or newly constructed by you) object, as Modified. Source: http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx
If you're unsure whether an object has been added or modified you can use the last segment's example:
context.Entry(customer).State = customer.Id == 0 ?
EntityState.Added :
EntityState.Modified;
You need to take these actions on all of the objects being added/modified, so if this object is complex and has other objects that need to be updated in the DB via FK relationships, you need to set their EntityState as well.
Depending on your scenario you can make these kinds of don't-care writes cheaper by using a different Context variation:
public class MyDb : DbContext
{
. . .
public static MyDb CheapWrites()
{
var db = new MyDb();
db.Configuration.AutoDetectChangesEnabled = false;
db.Configuration.ValidateOnSaveEnabled = false;
return db;
}
}
using(var db = MyDb.CheapWrites())
{
db.Entry(customer).State = customer.Id == 0 ?
EntityState.Added :
EntityState.Modified;
db.SaveChanges();
}
You're basically just disabling some extra calls EF makes on your behalf that you're ignoring the results of anyway.
B) Pessimistic. You can actually query the DB to verify the data hasn't changed/been added since you last picked it up, then update it if it's safe.
var existing = db.Customers.Find(customer.Id);
// Some logic here to decide whether updating is a good idea, like
// verifying selected values haven't changed, then
db.Entry(existing).CurrentValues.SetValues(customer);

In Entity Framework, take a newly created object and use it to update an existing record

Here's what I'd like to do:
var myCustomer = new Customer();
myCustomer.Name = "Bob";
myCustomer.HasAJob = true;
myCustomer.LikesPonies = false;
Then I'd like to pass it into an update method:
public UpdateCustomer(Customer cust)
{
using(var context = dbcontext())
{
var dbCust = context.Customers.FirstOrDefault(c => c.Name == cust.Name);
if(dbCust != null)
{
// Apply values from cust here so I don't have to do this:
dbCust.HasAJob = cust.HasAJob;
dbCust.LikesPonies = cust.LikesPonies
}
context.SaveChanges();
}
}
The reason for this is I'm working in multiple different parts of my application, and/or across DLLs. Is this possible?
EDIT: Found this question to be immensely useful:
Update Row if it Exists Else Insert Logic with Entity Framework
If you are sure that the entity is in the database and you have key you would just Attach the object you have to the context. Note that attached entities are by default in Unchanged state as the assumption is that all the values of properties are the same as in the database. If this is not the case (i.e. values are different) you need to change the state of the entity to modified. Take a look at this blog post: http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx it describes several sceanrios including the one you are asking about.