I see two codes for adding and deleting entity, I wonder which is the best way and what is the difference between these two.
One is this (for Adding):
using (var context = new BloggingContext())
{
var blog = new Blog { Name = "ADO.NET Blog" };
context.Blogs.Add(blog);
context.SaveChanges();
}
and another is this:
using (var context = new BloggingContext())
{
var blog = new Blog { Name = "ADO.NET Blog" };
context.Entry(blog).State = EntityState.Added;
context.SaveChanges();
}
and I read that calling the Add method on DbSet puts the entity into the Added state.
According to this I think above two codes are nearly same. If its not please tell me the difference.
And another code I've found is:
public virtual void Add(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State != EntityState.Added)
{
dbEntityEntry.State = EntityState.Added;
}
else
{
DbSet.Add(entity);
}
}
and if its true that calling the Add method on DbSet puts the entity into the Added state then I think there is no difference in code in if and else block, so what's the point in here.
And from the above three code which is the best way to add an Entity.
And another code in which I have doubt is what is the use of else block in below code:
public virtual void Delete(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State != EntityState.Deleted)
{
dbEntityEntry.State = EntityState.Deleted;
}
else
{
DbSet.Attach(entity);
DbSet.Remove(entity);
}
}
I don't see a huge benefit in setting the state of an entity to Added since creating a new entity and adding it to the set does exactly that like you mentioned. Where this type of pattern is pretty useful is when you want to delete an entity without having to fetch it from the database first:
// this entity will be unattached at this point, so if you were to call remove
// on the dbset it wouldn't do much, since it doesn't think it's in the database
var deleteThisEntity = new Blog { Id = 5 };
// if you set the state to deleted, it now thinks that it needs to be deleted
db.Entry(deleteThisEntity).State = EntityState.Deleted;
// call SaveChanges to delete
db.SaveChanges();
You can get a similar effect by setting the state to modified, so it will trigger an update statement. Sometimes you just don't want to take the extra hit of fetching an item from the database just to delete it.
ADD Patterns for ASP .NET using the code bellow is very standard practice.
using (var context = new BloggingContext())
{
var blog = new Blog { Name = "ADO.NET Blog" };
context.Blogs.Add(blog);
context.SaveChanges();
}
In the code for delete, the if/else statement is to check if the fetched object is valid.
The code I've been using for DELETE patterns is this:
var fetchedObject = context.Blog.Find(id);
if (fetchedObject == null)
{
return false;
}
else
{
ds.Blog.Remove(fetchedObject);
return true;
}
This is a method inside a Manager class that receives the id.
Related
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
}
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();
I have an MVC application with the following code in the POST method of the controller. I am doing an EF Add and obviously that is not right. I want it to add the record if it doesn't exist, otherwise Update. How can I do that please?
try
{
AttributeEntities db = new AttributeEntities();
IEnumerable<string> items = viewModel.SelectedAttributes2;
int i = 0;
foreach (var item in items)
{
var temp = item;
// Save it
SelectedHarmonyAttribute attribute = new SelectedHarmonyAttribute();
attribute.CustomLabel = viewModel.ItemCaptionText;
attribute.IsVisible = viewModel.Isselected;
string harmonyAttributeID = item.Substring(1, 1);
// attribute.OrderNumber = Convert.ToInt32(order);
attribute.OrderNumber = i++;
attribute.HarmonyAttribute_ID = Convert.ToInt32(harmonyAttributeID);
db.SelectedHarmonyAttributes.Add(attribute);
db.SaveChanges();
}
}
You would need to check the database for the record you are trying to add/update. If the look-up returns null, that means that it doesn't exist in the database. If it does, you can modify the record that you looked up and call db.SaveChanges() to persist the changes you made to the database.
Edit:
int id = Convert.ToInt32(harmonyAttributeID);
var existingEntry = db.SelectedHarmonyAttributes.SingleOrDefault(x => x.HarmonyAttribute_ID == id);
One common way to determine an add or update is by simply looking at an identifier field, and setting the appropriate state.
using System.Data;
SelectedHarmonyAttribute attribute;
using (var db = new YourDbContext())
{
db.Entry(attribute).State = attribute.HarmonyAttribute_ID == 0 ? EntityState.Added : EntityState.Modified;
db.SaveChanges();
}
You could import the System.Data.Entity.Migrations namespace and use the AddOrUpdate extension method:
db.SelectedHarmonyAttributes.AddOrUpdate(attribute);
db.SaveChanges();
EDIT:
I'm assuming that SelectedHarmonyAttributes is of type DbSet
EDIT2:
Only drawback with doing it this way (and it may not be a concern for you), is that your entity isn't responsible for it's own state change. This means that you can update any property of the entity to something invalid, where you might want to internally validate it on the entity itself or maybe do some other processing you always want to occur on update. If these things are a concern for you, you should add a public Update method onto the entity and check for its existence on the database first. e.g:
var attribute = db.SelectedHarmonyAttributes.SingleOrDefault(x => x.HarmonyAttribute_ID == harmonyAttributeID);
if (attribute != null)
{
attribute.Update(viewModel.ItemCaptionText, viewModel.Isselected, i++);
}
else
{
attribute = new Attribute(viewModel.ItemCaptionText, viewModel.Isselected);
db.SelectedHarmonyAttributes.Add(attribute);
}
db.SaveChanges();
Your update method might look something like:
public void Update(string customLabel, bool isVisible, int orderNumber)
{
if (!MyValidationMethod())
{
throw new MyCustomException();
}
CustomLabel = customLabel;
IsVisible = isVisible;
OrderNumber = orderNumber;
PerformMyAdditionalProcessingThatIAlwaysWantToHappen();
}
Then make all of the entities' properties public "get" but protected "set" so they can't be updated from outside the entity itself. This might be going off an a bit of a tangent but using the AddOrUpdate method would assume you don't want to control the way an update occurs and protect your domain entity from getting into an invalid state etc. Hope this helps!
asp.net mvc 5, entity framework 6.0. I have ADO.NET EDM model. And i need update multiple entities. If i use Entry several times, i take exception: ObjectStateManager already have an object with the same key.
How i can do this, using something like this code:
db.Entry(company.Company).State = EntityState.Modified; //one type object
db.SaveChanges();
db.Entry(company.Preview).State = EntityState.Modified; //another type object
db.SaveChanges();
foreach (CompanyTelephone item in company.Phones) // another type
{
if (item.Id > 0)
{
db.Entry(item).State = EntityState.Modified;
db.SaveChanges();
}
else
{
db.CompanyTelephones.Add(item);
}
}
db.SaveChanges();
You can use Entry as often as you want. The problem is in setting the state. If you set the state the item is attached to the context. Apparently, some items were already attached in the first two lines. You can simply check whether an item is Detached:
if (item.Id > 0 && db.Entry(item).State == EntityState.Detached)
{
db.Entry(item).State = EntityState.Modified;
db.SaveChanges();
}
else
{
db.CompanyTelephones.Add(item);
}
Still, as Andrei said, multiple SaveChanges calls should not be necessary. You break the transactional unit. Maybe it doesn't matter. Nevertheless, esp. the calls within the loop cause a lot of separate database rondtrips.
I have the following update function
public void UpdateBatchDefinition(BatchDefinition batchToUpdate)
{
if (batchToUpdate == null)
{
throw new ArgumentNullException("batchToUpdate");
}
BatchDefinition foundDefinition =
this.context.BatchDefinitions.SingleOrDefault(definition => definition.Id == batchToUpdate.Id);
if (foundDefinition != null)
{
if (!string.IsNullOrWhiteSpace(batchToUpdate.Name))
{
foundDefinition.Name = batchToUpdate.Name;
}
if (!string.IsNullOrWhiteSpace(batchToUpdate.Description))
{
foundDefinition.Description = batchToUpdate.Description;
}
if (!string.IsNullOrWhiteSpace(batchToUpdate.LoadType))
{
foundDefinition.LoadType = batchToUpdate.LoadType;
}
if (batchToUpdate.JobId != Guid.Empty)
{
foundDefinition.JobId = batchToUpdate.JobId;
}
foundDefinition.Tables = batchToUpdate.Tables;
this.context.SaveChanges();
}
}
the issue I am having Is when I am trying to update the Tables list. Tables is a List of Table and Table is a Entity of another table
Tables could be added to, removed from or left alone. I need to update that with what ever is being passed in
when I run this right now I get an 'EntityValidationErrors' error, though it wont tell me what the validation issue actually is.
on Inserting I got the same error but was able to fix it using the following
var underlyingContext = this.context as DbContext;
if (underlyingContext != null)
{
foreach (var table in batchDefinition.Tables)
{
// Need to mark the table entity as unchanged or
// else EF will treat it as a new table
underlyingContext.Entry(table).State = EntityState.Unchanged;
}
}
so I tried using that in this update function
var underlyingContext = this.context as DbContext;
if (underlyingContext != null)
{
foreach (var table in foundDefinition.Tables)
{
// Need to mark the table entity as unchanged or
//else EF will treat it as a new table
underlyingContext.Entry(table).State = EntityState.Unchanged;
}
}
foundDefinition.Tables = batchToUpdate.Tables;
and I get the following error instead:
AcceptChanges cannot continue because the object's key values conflict
with another object in the ObjectStateManager. Make sure that the key
values are unique before calling AcceptChanges.
Any thoughts one what I am missing here?
Change end of your update method like this:
foreach (var t in foundDefinition.Tables.ToList())
Context.Tables.Remove(t);
foundDefinition.Tables = batchToUpdate.Tables;
this.context.SaveChanges();
And about your last error, it is said that there are some duplicates in your context. So, EF can't save the context changes into the db (because there are duplicates in the context!)
In fact, I don't know the last error is from add or delete - you didn't mention clearly. So, I don't know the last two code samples are from your add method, or your update method...
However for update, the trick I mentioned here, must solve your problem for update...