Updating an entity with required properties in Entity Framework - c#

I realise that updating entities without first selecting them is a common problem and many solutions are already on StackOverflow, however after reading these I'm still having a problem.
I'm using the following code to update a User entitiy:
using (var context = GetContext())
{
var userEntity = new UserEntity() { ID = userUpdate.ID };
context.Users.Attach(userEntity);
context.Entry(userEntity).CurrentValues.SetValues(userUpdate);
context.SaveChanges();
}
However this results in a DbEntityValidationException being thrown because my User entitiy has some required properties but these aren't necessarily set on the updated entity.
Is there any way around this or is it simply a case of removing the required properties?
Thanks!

I've found an answer here: Entity Framework/MVC3: temporarily disable validation
By temporarily disabling validation I can bypass the checks and insert any number of values without retrieving the required properties first:
using (var context = GetContext())
{
var userEntity = new UserEntity() { ID = userUpdate.ID };
context.Users.Attach(userEntity);
context.Entry(userEntity).CurrentValues.SetValues(userUpdate);
// Disable entity validation
context.Configuration.ValidateOnSaveEnabled = false;
context.SaveChanges();
}

If you only want to update particular fields in your entity without having to retrieve the entire thing from the database first:
var userEntity = new UserEntity() { ID = userUpdate.ID };
userEntity.SomeProperty = userUpdate.SomeProperty;
//Tell EF to only update the SomeProperty value:
context.Entry(userEntity).Property(x => x.SomeProperty).IsModified = true;
context.SaveChanges();

Related

Best way to update few columns in entity framework on disconnected scenario

I want to update few columns on my table.
public void LogOutUser(int systemUserId)
{
DatabaseContext context = new DatabaseContext ();
var user = new SystemUser()
{
SystemUserId = systemUserId,
IsLocked = true,
IsLoggedIn = false
};
context.SystemUsers.Attach(user);
context.Entry(user).Property(x => x.IsLocked ).IsModified = true;
/context.Entry(user).Property(x => x.IsLoggedIn ).IsModified = true;
context.SaveChanges();
}
It's giving me an error "Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.". I am aware that this error happens because I have some Properties in my "SystemUsers" class that are required, like Email, FullName, etc. I could have easily done it when I use connected scenario..
var user = context.SystemUsers.Find(systemUserId);
user.IsLocked = true;
user.IsLoggedIn = false;
context.SaveChanges();
But I don't want to use this way as I believe it's a waste of resources as I have to fetch the whole row first then update it. Now, my question is, what is the best way to update EF using disconnected scenario given that I only have "systemUserId" as a way of telling the EF that this data is existing in my database.
I found the solution for this problem. I just added this line code before SaveChanges();
context.Configuration.ValidateOnSaveEnabled = false;
ValidateOnSaveEnabled is set true by default, and making this false would not require EF to validate your entities before saving.

EF Core No tracking and Add() or Update()

I'm trying to make my DbContext to work without tracking.
public class MyContext: DbContext
{
public MyContext()
{
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
}
...
}
Also, after every Add(..) or Update(..) I remove the tracking of the new entity:
_context.Users.Add(user);
await _context.SaveChangesAsync();
_context.Entry(user).State = EntityState.Detached;
The problem arises if I add (or update) a new entity that has a reference to an already existing entity (meaning already stored in the database).
For example:
var section = new Section();
_context.Sections.Add(section);
await _context.SaveChangesAsync();
_context.Entry(section).State = EntityState.Detached;
...
var user = new User
{
Name = "Alex",
Section = section
}
_context.Users.Add(user);
await _context.SaveChangesAsync();
Results in an error:
System.ArgumentException: 'An item with the same key has already been added. Key: 1'
How can I fix this?
I had a similar problem in my Blazor server app with the Entity framework.
Even if I detach the saved object, referenced property object was still tracked.
Instead of detaching I'm calling a clear function on the tracker:
_dbContext.ChangeTracker.Clear();
I think you can solve this problem in two ways, depending on what you do with the newly added entity afterwards:
Re-attach the section before using it for a new user:
_context.Sections.Attach(section)
var user = new User
{
Name = "Alex",
Section = section
}
Only add the section's primary key to the user, e.g. for a 1-n relation:
var user = new User
{
Name = "Alex",
SectionId = section.Id
}

Issue with Entity Framework 7 Shadow Property

This is EF 7 (now Core). I have a shadow property named CreatedBy. EF correctly loads its value from data store, the value of this property remains accessible as long as I use the same DbContext instance but I need to work in detached way, and submit changes later using another DbContext instance.
The problem is that for subsequent DbContexts all shadows properties are NULL, and so far I cannot see anyway to get it loaded. So before get rid of this shadows I need to know if somebody already have come across for a solution for this issue.
If it could help, here is how proceeding with here:
var cached = Cache.Get<MyType>();
cached.Default = false; //some updating
var dbContext = new MyContext();
dbContext.Attach( cached );
dbContext.Entry( cached ).State = EntityState.Modified;
dbContext.SaveChanges();
SaveChanges is submitting NULL value for shadows property.
You need to attach the cached object directly to the table/object it belongs to instead of directly to the context. For example:
var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };
using (var context = new BloggingContext())
{
context.Blogs.Attach(existingBlog);
context.Entry(existingBlog).State = EntityState.Modified;
context.SaveChanges();
}
You are doing context.Attach() directly and this will not work. Go here, https://msdn.microsoft.com/en-us/data/jj592676.aspx, for more info.

How to remove related data

I have an MVC Edit page, on which I also edit related data.
On postback, I have the following piece of code that handles my inserts, updates and deletes for related data:
if (ModelState.IsValid)
{
Guid myGuid = new Guid();
//-- Set PK and FK values --
foreach (AccountType_Organisatie myAccType_Org in myOrg.AccountType_Organisaties)
{
if (myAccType_Org.AccountType_Organisatie_GUID == myGuid)
{
//-- Create --
myAccType_Org.AccountType_Organisatie_GUID = Guid.NewGuid();
myAccType_Org.Organisatie_GUID = myOrg.Organisatie_GUID;
csContext.Entry(myAccType_Org).State = EntityState.Added;
}
else
{
//-- Update --
//-- Force MOdified state - even when unchanged --
csContext.Entry(myAccType_Org).State = EntityState.Modified;
}
//myAccType_Org.AccountType = csContext.AccountTypes.Find(myAccType_Org.Accounttype_GUID);
}
//-- Explicitly handle Deletes --
List<Guid> lGuids = myOrg.AccountType_Organisaties.Select(p => p.AccountType_Organisatie_GUID).ToList();
csContext.AccountType_Organisaties.RemoveRange(csContext.AccountType_Organisaties.Where(p => p.Organisatie_GUID == myOrg.Organisatie_GUID).Where(q => !lGuids.Contains(q.AccountType_Organisatie_GUID)));
csContext.Entry(myOrg).State = EntityState.Modified;
csContext.SaveChanges();
return RedirectToAction("Index");
}
I am explicitly deleting items from the context that are no longer present in the new model (posted back from edit page). Is this the best way to do this?
I am explicitly setting the State to modified for all (still) existing items, forcing an update - what is the best way to determine whether an update is necessary or not? Do I need to explicitly compare the new values of the item in the model to the item in the database?
Some help / guidelines on how to implement the above is appreciated.

Updating Database via Anonymous Type?

The following code gets all the rows from my Activities table that have not already been posted on Twitter. It then loops through and posts Twitter updates for each of those row. In the process, I would like to update the database to indicate these rows have now been "twittered".
However, I'm getting an error (indicated below) when I try and update this value. I assume this is because I'm using an anonymous type. However, if I use the full type, that will require pulling a lot of unnecessary data from the database.
Is there a way to accomplish this efficiently? Or is this yet another case where EF forces me to make compromises in performance?
using (MyEntities context = new MyEntities())
{
var activities = from act in context.Activities
where act.ActTwittered == false
select new { act.ActID, act.ActTitle, act.Category,
act.ActDateTime, act.Location, act.ActTwittered };
foreach (var activity in activities)
{
twitter.PostUpdate("...");
activity.ActTwittered = true; // <== Error: ActTwittered is read-only
}
}
You could try a "fake object approach" like this:
using (MyEntities context = new MyEntities())
{
var activities = from act in context.Activities
where act.ActTwittered == false
select new { act.ActID, act.ActTitle, act.Category,
act.ActDateTime, act.Location, act.ActTwittered };
foreach (var activity in activities)
{
twitter.PostUpdate("...");
// Create fake object with necessary primary key
var act = new Activity()
{
ActID = activity.ActID,
ActTwittered = false
};
// Attach to context -> act is in state "Unchanged"
// but change-tracked now
context.Activities.Attach(act);
// Change a property -> act is in state "Modified" now
act.ActTwittered = true;
}
// all act are sent to server with sql-update statements
// only for the ActTwittered column
context.SaveChanges();
}
It's "theoretical" code, not sure if it would work.
Edit
Not "theoretical" anymore. I've tested this with DbContext of EF 4.1 and it works as described in the sample code above. (Because DbContext is only a wrapper API around ObjectContext it's almost safe to assume that it also will work in EF 4.0.)
If you simply select 'act', then it should work. Don't forget to submit after editing.
Why are you calling select new instead of returning entire object. Entity framework will only be able to update property if it is correctly defined in schema resources which certainly is not case with anonymous type.
Entity framework will never be able to determine which table and which field the property is mapped to.

Categories