EF Database context not saving update - entity-framework

I have some simple code I am using to update a profile picture that is saved in my database as a byte[]. For some odd reason it runs through my code with no errors at all but the database is not updated with the new picture that the user has chosen.
Client client = db.Clients.FirstOrDefault(c => c.Email == User.Identity.Name);
if (client != null)
{
client.ProfileImage = bytes;
db.Clients.Attach(client);
var entry = db.Entry(client);
entry.Property(e => e.ProfileImage).IsModified = true;
db.SaveChanges();
}
Here is the code for the Client entity class:
public partial class Client
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Client()
{
Customers = new HashSet<Customer>();
}
public int ID { get; set; }
public int? StripeID { get; set; }
[Required]
[StringLength(200)]
public string Name { get; set; }
[Required]
[StringLength(150)]
public string Email { get; set; }
public Guid UserID { get; set; }
public bool IsActive { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public byte[] ProfileImage { get; set; }
public virtual StripeInfo StripeInfo { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Customer> Customers { get; set; }
}
Also after debugging the code I can see that the profile image is being set to the correct byte[] and the client.ProfileImage then contains the correct byte[], but as soon as db.SaveChanges() is called, client.ProfileImage is immediately set back to what it originally was. No errors occur or anything so I really can't figure out why this would be happening.
7/4/2017
Here are some additional things I have tried after looking on the internet some more:
1) Create a new database context instance to make the update.
2) Dispose of the original database context and create a new one to make the update.
Neither of the above attempts have worked. The ProfileImage property is still just set back to the original value when db.SaveChanges() is called.

The issue is that the ProfileImage property of your entity has an attribute applied to it:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
Which, from the documentation, is telling Entity Framework:
Computed: The database generates a value when a row is inserted or updated.
You just need to remove that attribute completely.

Can you try with below code ? :
Client client = db.Clients.FirstOrDefault(c => c.Email == User.Identity.Name);
if (client != null)
{
client.ProfileImage = bytes;
var entry = db.Entry(client);
entry.Property(e => e.ProfileImage).IsModified = true;
db.SaveChanges();
}
If client has been already loaded from context then no need to attach it !

Related

Entity Framework and RESTful WebAPI - possible circular reference

Here is a simplified version of my model:
public class User {
public int UserID { get; set; }
public string FirstName { get; set; }
public virtual ICollection<Recipe> Recipes { get; set; }
}
public class Recipe {
public int RecipeID { get; set; }
public string RecipeName { get; set; }
public int UserID { get; set; }
public virtual User User { get; set; }
}
I have a controller that I'd like to return a User as well as some summary information about their recipes. The scaffolded controller code looks like this:
var user = await _context.Users.SingleOrDefaultAsync(m => m.UserID == id);
It works fine. Now I try to add the Recipes, and it breaks:
var user = await _context.Users.Include(u => u.Recipes).SingleOrDefaultAsync(m => m.UserID == id);
My web browser starts to render the JSON, and it flickers and I get a message in the browser saying the connection has been reset.
My Theory - I believe that the parent (User) renders, which exposes the child (Recipe) which contains a reference to the parent (User), which contains a collection of the child (Recipe) and so on which is causing an infinite loop. Here's why I think this is happening:
The Visual Studio debugger allows me to navigate the properties in that way infinitely.
If I comment out the Recipe.User property, it works fine.
What I've tried
I tried to just include the data from Recipe that I need using Entity Framework projection (I'm attempting to not include Recipe.User). I tried to only include Recipe.RecipeName... but when I try to use projection to create an anonymous type like this:
var user = await _context.Users.Include(u => u.Recipes.Select(r => new { r.RecipeName })).SingleOrDefaultAsync(m => m.UserID == id);
I receive this error:
InvalidOperationException: The property expression 'u => {from Recipe r in u.Recipes select new <>f__AnonymousType1`1(RecipeName = [r].RecipeName)}' is not valid. The expression should represent a property access: 't => t.MyProperty'.
What is the solution? Can I project with different syntax? Am I going about this all wrong?
Consider using POCOs for serialization rather than doubly-linked entity classes:
public class UserPOCO {
public int UserID { get; set; }
public string FirstName { get; set; }
public ICollection<RecipePOCO> Recipes { get; set; }
}
public class RecipePOCO {
public int RecipeID { get; set; }
public string RecipeName { get; set; }
public int UserID { get; set; }
}
Copy the entity contents to the corresponding POCO and then return those POCO objects as the JSON result. The removal of the User property via usage of the RecipePOCO class will remove the circular reference.
I can propose you 3 options.
U sing [JsonIgnore] on property, but it will work on every use of Recipe class, so when you would like to just return Recipe class you won't have User in it.
public class Recipe {
public int RecipeID { get; set; }
public string RecipeName { get; set; }
public int UserID { get; set; }
[JsonIgnore]
public virtual User User { get; set; }
}
You can this solution to stop reference loop in all jsons https://stackoverflow.com/a/42522643/3355459
Last option is to create class (ViewModel) that will only have properties that you want send to the browser, and map your result to it. It is propably best from security reason.

Asp.net core 2.0 The instance of entity type 'X' cannot be tracked because another instance with the key value 'Id:x' is already being tracked

I have been working on a shop site project, using asp.net core spa templates provided with the latest VS2017, and have come across an issue that I haven't had before, possibly because until now my apps were quite simple!
I know what the problem is and where, I just can't fix it. I have a product model which has a collection of "Attributes" and a collection of "Variations" (different colour size, etc) and those variations also have attributes, so if the same Attribute shows up in the Variation (VAttributes), as is already in the main "Attributes" I get the error
InvalidOperationException: The instance of entity type
'ProductAttribute' cannot be tracked because another instance with the
key value 'Id:2' is already being tracked. When attaching existing
entities, ensure that only one entity instance with a given key value
is attached.
The best answer I found was here : https://stackoverflow.com/a/19695833/6749293
Unfortunately, even with the above check I got the error, I even tried making a list of attached attributes, and if the vattribute matched one of the items in the list, I didn't attach it. In fact I found that even if I don't attach (_context.attach()) any of the vAttributes, it still throws the error!.
Here's the code in question:
public async Task<Product> Create(Product product)
{
try
{
foreach (var variation in product.Variations)
{
foreach (var vAttr in variation.VAttributes)
{
bool isDetached = _context.Entry(vAttr).State == EntityState.Detached;
if (isDetached)
_context.Attach(vAttr);
}
}
foreach (var attribute in product.Attributes)
{
bool isDetached = _context.Entry(attribute).State == EntityState.Detached;
if (isDetached)
_context.Attach(attribute);
}
foreach (var category in product.Categories)
{
_context.Attach(category);
_context.Attach(category).Collection(x => x.Children);
}
_context.Products.Add(product);
await Save();
return product;
}
catch (Exception)
{
throw;
}
}
The models for the 3 objects are as follows:
public class Product
{
[Key, DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Description { get; set; }
public string StockRef { get; set; }
public DateTime? LastModified { get; set; }
//image needed
public ICollection<ProductCategory> Categories { get; set; }
public ICollection<ProductAttribute> Attributes { get; set; }
public ICollection<ProductVariation> Variations { get; set; }
public Product()
{
Attributes = new List<ProductAttribute>();
Variations = new List<ProductVariation>();
Categories = new List<ProductCategory>();
}
}
Variation:
public class ProductVariation
{
[Key, DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public DateTime? LastModified { get; set; }
public virtual ICollection<ProductAttribute> VAttributes { get; set; }
//needs images
public decimal VPrice { get; set; }
public string VStockRef { get; set; }
}
Finally the Attribute:
public class ProductAttribute
{
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("AttributeCategory")]
public int AttributeCategoryId { get; set; }
public virtual AttributeCategory AttributeCategory { get; set; }
}
Most help I found when searching was more related to having repo's injected as singletons, or HttpPut methods where the code had check for existence omitting the .AsNoTracking() or it was a mistake that they had the second instance in some way, where I am aware of the second instance, I just don't know how to prevent it from being tracked!
EDIT: I found that adding a foreign key on the ProductVariation model to the Product that was being created failed as it was only a temp key!? anyway removed it from the variation model, so have updated my code. Also thought I'd add one of my earler failed attempts, that led to all of the foreach loops.
_context.AttachRange(product.Attributes);
_context.AttachRange(product.Categories);
_context.AttachRange(product.Variations);
_context.Add(product);
I believe you can allow EF to handle the tracking.
public virtual bool Create(T item)
{
try
{
_context.Add(item);
_context.SaveChanges();
return true;
}
catch (Exception e)
{
return false;
}
}
This allows for you to save the entire object structure without worring about attaching items.
var newProduct = new Product();
newProduct.Categories.Add(cat);
newProduct.Attributes.Add(att);
newProduct.Variations.Add(vari);
Create(newProduct);

Save partial data from model to db MVC4

I have a model
public partial class User
{
public int user_id { get; set; }
public virtual string username { get; set; }
public string password { get; set; }
public string salt { get; set; }
public string email { get; set; }
public sbyte status { get; set; }
public System.DateTime creation_date { get; set; }
}
i wanto to give the user the possibility to change his mail from a page
so i create my controller
[Authorize]
[HttpGet]
public ActionResult Setup()
{
//retrieve the object user
return View(CustomDbFunctions.GetUserEntity(User, db));
}
then in my view
#Html.EditorFor(u => u.email)
but when i save my modification in the db using
[HttpPost]
public ActionResult Setup(User user)
{
if (ModelState.IsValid)
{
db.Entry(user).State = System.Data.EntityState.Modified;
db.SaveChanges();
return Redirect("Index");
}
return View(user);
}
the system return an error, because the object user contains ONLY the modified mail. All others properties (i.e. User_id, password) are gone.
How can i send back to the db the entire object properly modified?
To Update something with entity framework you have to first get the entity from DB:
if (ModelState.IsValid)
{
var existingUser = db.Users.FirstOrDefault(u => u.user_id == user.user_id);
existingUser.email = user.email;
db.SaveChanges();
return Redirect("Index");
}
For this to work you also need to have in your view, inside the form, something like
#Html.HiddenFor(u => u.user_id)
However, I suppose you have the Id of the user somewhere in the Session. You should use it from there and not from the hidden field, because a hacker could change the value in the hidden tag and edit another user's email.
If you use model binding of mvc, then you need to use the other properties as hidden field, that way while posting back the whole object will be returned instead of only email.

Setting EntityState.Modified during update operation with Entity Framework

Assume that I have the following little console application which uses Entity Framework 5:
class Program {
static void Main(string[] args) {
using (var ctx = new ConfContext()) {
var personBefore = ctx.People.First();
Console.WriteLine(personBefore.Name);
personBefore.Name = "Foo2";
ctx.SaveChanges();
var personAfter = ctx.People.First();
Console.WriteLine(personAfter.Name);
}
Console.ReadLine();
}
}
public class ConfContext : DbContext {
public IDbSet<Person> People { get; set; }
public IDbSet<Session> Sessions { get; set; }
}
public class Person {
[Key]
public int Key { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public DateTime? BirthDate { get; set; }
public ICollection<Session> Sessions { get; set; }
}
public class Session {
[Key]
public int Key { get; set; }
public int PersonKey { get; set; }
public string RoomName { get; set; }
public string SessionName { get; set; }
public Person Person { get; set; }
}
As you can see, I am changing the name of the record and saving it. It works but it feels like magic to me. What I am doing in all of my applications is the following one (to be more accurate, inside the Edit method of my generic repository):
static void Main(string[] args) {
using (var ctx = new ConfContext()) {
var personBefore = ctx.People.First();
Console.WriteLine(personBefore.Name);
personBefore.Name = "Foo2";
var entity = ctx.Entry<Person>(personBefore);
entity.State = EntityState.Modified;
ctx.SaveChanges();
var personAfter = ctx.People.First();
Console.WriteLine(personAfter.Name);
}
Console.ReadLine();
}
There is no doubt that the second one is more semantic but is there any other obvious differences?
Well the second code block where you explicitly set the entity state is redundant, as the change tracker already knows that the entity is modified because the context knows about the entity (as you query the context to retrieve the entity).
Setting (or painting) the state of the entity would be more useful when working with disconnected entities, for example in an n-tier environment where the entity was retrieved in a different context and sent to a client for modification, and you wish to mark those changes back on the server using a different context.
Otherwise, the first code block is cleaner in my opinion.

MVC 3 EF 4.1 dbContext - Deleting one-to-many data object with non-nullable foreign-key relation

I am using MVC 3, EF 4.1, and dbContext. I need to know how to delete an entity in one-to-many relation with a non-nullable foreign-key.
When I Remove the child entity and execute SaveChanges I get the error:
The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
From other posts, I understand that using Remove(entity) marks the entity for delete. During SaveChanges, EF sets the the foreign-key to Null and the above error occurs.
I have found some posts that use DeleteObject on the child entity rather than Remove; however, the DeleteObject approach seems to have been dropped because of addition to dbContext and DbSet.
I have found posts that suggest modifying the EDMX foreign-key relation to be Nullable. Modifying the EDMX is fine, but whenever an Update Model for Database is done, these changes get nuked and must be reapplied. Not optimal.
Another post suggested creating a proxy entity with the foreign-key relations set to Nullable but I do not understand that approach. It seems to suffer from the same issue as modifying the EDMX in that the context gets automatically updated when changes to the EDMX are saved.
My simplified model is:
public partial class User
{
public User()
{
this.UserContacts = new HashSet<UserContact>();
}
public long userId { get; set; }
public string userEmail { get; set; }
public string userPassword { get; set; }
public string userFirstName { get; set; }
public string userLastName { get; set; }
. . .
public virtual Country Country { get; set; }
public virtual State State { get; set; }
public virtual ICollection<UserContact> UserContacts { get; set; }
}
}
public partial class UserContact
{
public long userContactId { get; set; }
public long userContactUserId { get; set; }
public long userContactTypeId { get; set; }
public string userContactData { get; set; }
public virtual ContactType ContactType { get; set; }
public virtual User User { get; set; }
}
The userContactUserId and userContactTypeId are required foreign-keys.
In the dbContext container both Users and UserContact are DbSet.
I have a ViewModel for the User and a ViewModel for UserContact as follows
public class UserContactViewModel
{
[HiddenInput]
public long UserContactId { get; set; }
[HiddenInput]
public long UserContactUserId { get; set; }
[Display(Name = "Contact")]
[Required]
public string ContactData { get; set; }
[Required]
public long ContactType { get; set; }
[HiddenInput]
public bool isDeleted { get; set; }
}
public class MyProfileViewModel
{
[HiddenInput]
public long UserId { get; set; }
[Required]
[Display(Name = "First Name")]
[StringLength(100)]
public string FirstName { get; set; }
[Required]
[StringLength(100)]
[Display(Name = "Last Name")]
public string LastName { get; set; }
....
public IEnumerable<UserContactViewModel> Contacts { get; set; }
}
When saving changes to the user profile, I loop over the list of UserContactViewModel entities to determine which have been added, modified, or deleted.
foreach (var c in model.Contacts)
{
UserContact uc = usr.UserContacts.Single(con => con.userContactId == c.UserContactId);
if (uc != null)
{
if (c.isDeleted == true) // Deleted UserContact
{
ctx.UserContacts.Remove(uc); // Remove doesn't work
}
else // Modified UserContact
{
uc.userContactData = c.ContactData;
uc.userContactTypeId = c.ContactType;
ctx.Entry(uc).State = EntityState.Modified;
}
}
else // New UserContact
{
usr.UserContacts.Add(new UserContact { userContactUserId = model.UserId, userContactData = c.ContactData, userContactTypeId = c.ContactType });
}
}
I'd appreciate any help.
I managed to solve the problem as follows:
First, I was able to fetch the ObjectContext by casting my DbContext (eg "ctx") to an IObjectContextAdapter and then obtaining reference to the ObjectContext.
Next, I simply called the DeleteObject method passing the UserContact record to be deleted.
When SaveChanges gets the deletes in the database happen as expected.
if (c.isDeleted == true) // Deleted UserContact
{
ObjectContext oc = ((IObjectContextAdapter)ctx).ObjectContext;
oc.DeleteObject(uc)
}
Here is a snippet of the relevant code:
foreach (var c in model.Contacts)
{
UserContact uc = null;
if (c.UserContactId != 0)
{
uc = ctx.UserContacts.Find(c.UserContactId);
}
if (uc != null)
{
if (c.isDeleted == true) // Deleted UserContact
{
ObjectContext oc = ((IObjectContextAdapter)ctx).ObjectContext;
oc.DeleteObject(uc);
}
else // Modified UserContact
{
uc.userContactData = c.ContactData;
uc.userContactTypeId = c.ContactType;
ctx.Entry(uc).State = EntityState.Modified;
}
}
else // New UserContact
{
usr.UserContacts.Add(new UserContact { userContactData = c.ContactData, userContactTypeId = c.ContactType });
}
}
ctx.Entry(usr).State = EntityState.Modified;
ctx.SaveChanges();
Hope this helps someone in future.
I solved the same problem following the section "Cascade Delete Rules on Relationships" at the MSDN guide page here http://msdn.microsoft.com/en-us/library/bb738695.aspx
Hope tu be helpfull :D
well, implement your own ICollection and mark those child objects for deletion along with removing it. And then, in your own SaveChanges method override, delete those objects.
You can configure the relation to cascade ... this will propagate the delete to dependent entities.
But it's very dangerous :)
I prefer setting a flag in the row that prevents the data tier from including it in future queries, most applications do not need physical delete (and will be a chance to undo).