(This question has been asked on SO and i have read most of the related posts and try to implement based on the suggestions but still not working)
I am using EF6 ( not EF Core)
I am also using DB first approach. So we have .edmx file and C# entities are created by edmx template ( not sure if that matters here)
I want to update certain properties of an entity without loading the entire entity.
private async Task Monitor()
{
var timeStamp = DateTime.UtcNow.AddHours(-8);
var documents = await _dbContext.Documents
.Where(x => x.DocumentCreatedDateTime < timeStamp)
.Select(x => new
{
x.DocumentID,
x.DocumentCreatedDateTime,
x.ProcessStatusID,
ProcessStatus = x.ProcessStatus.ProcessStatusName,
x.CurrentErrors,
x.ModifiedDateTime,
x.VersionStamp
})
.ToListAsync();
if (documents.Count == 0)
{
return;
}
foreach (var document in documents)
{
var docEntity = new Document();
docEntity.DocumentID = document.DocumentID;
docEntity.CurrentErrors = "Document has error";
docEntity.ProcessStatusID = (int)StatusEnum.Error;
docEntity.ModifiedDateTime = DateTime.UtcNow;
docEntity.VersionStamp = document.VersionStamp;
_dbContext.Documents.Attach(docEntity);
var entry = _dbContext.Entry(docEntity);
entry.Property(p => p.CurrentErrors).IsModified = true;
entry.Property(p => p.ProcessStatusID).IsModified = true;
entry.Property(p => p.ModifiedDateTime).IsModified = true;
entry.Property(p => p.VersionStamp).IsModified = true;
}
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
}
Issue
The document entity has several other properties (columns) that are required in the database. But this particular process does not need to update those properties. When SaveChanges() get invoked i get EntityValidationErrors error
Update 1
I think i can do db.Configuration.ValidateOnSaveEnabled = false but not sure is that is the correct approach
The xxxxx field is required.
Related
var fdPositions = dbContext.FdPositions.Where(s => s.LastUpdated > DateTime.UtcNow.AddDays(-1));
foreach (JProperty market in markets)
{
// bunch of logic that is irrelevant here
var fdPosition = fdPositions.Where(s => s.Id == key).FirstOrDefault();
if (fdPosition is not null)
{
fdPosition.OddsDecimal = oddsDecimal;
fdPosition.LastUpdated = DateTime.UtcNow;
}
else
{
// bunch of logic that is irrelevant here
}
}
await dbContext.SaveChangesAsync();
This block of code will make 1 database call on this line
var fdPosition = fdPositions.Where(s => s.Id == key).FirstOrDefault();
for each value in the loop, there will be around 10,000 markets to loop through.
What I thought would happen, and what I would like to happen, is 1 database call is made
var fdPositions = dbContext.FdPositions.Where(s => s.LastUpdated > DateTime.UtcNow.AddDays(-1));
on this line, then in the loop, it is checking against the local table I thought I pulled on the first line, making sure I still properly am updating the DB Object in this section though
if (fdPosition is not null)
{
fdPosition.OddsDecimal = oddsDecimal;
fdPosition.LastUpdated = DateTime.UtcNow;
}
So my data is properly propagated to the DB when I call
await dbContext.SaveChangesAsync();
How can I update my code to accomplish this so I am making 1 DB call to get my data rather than 10,000 DB calls?
Define your fdPositions variable as a Dictionary<int, T>, in your query do a GroupBy() on Id, then call .ToDictionary(). Now you have a materialized dictionary that lets you index by key quickly.
var fdPositions = context.FdPositions.Where(s => s.LastUpdatedAt > DateTime.UtcNow.AddDays(-1))
.GroupBy(x=> x.Id)
.ToDictionary(x=> x.Key, x=> x.First());
//inside foreach loop:
// bunch of logic that is irrelevant here
bool found = fdPositions.TryGetValue(key, out var item);
The property 'Tags' on type 'Post' is not a primitive or complex property. The Property method can only be used with primitive or complex properties. Use the Reference or Collection method.
I am using this code snippets for update a post text and related tags:
var tags = TagConverter.FetchTags(postText);
var newTags = tags.Select(t => t.Title);
var savedTags = _db.Tags.Where(t => newTags.Contains(t.Title)).ToList();
var notSavedTags = tags.Where(t => savedTags.All(st => st.Title != t.Title)).ToList();
var insertedTags = _db.Tags.AddRange(notSavedTags).ToList();
insertedTags.AddRange(savedTags);
_db.SaveChanges();
var updatedPost = post;
updatedPost.Title = postText;
updatedPost.Tags = insertedTags;
_db.Posts.Attach(updatedPost);
var entry = _db.Entry(updatedPost);
entry.Property(e => e.Title).IsModified = true;
entry.Property(e => e.Tags).IsModified = true;
_db.SaveChanges();
How to update tags?
Setting IsModified works for single entity, not for a collection. This is why this line doesn't work:
entry.Property(e => e.Tags).IsModified = true;
As you already attached all tags to context, you need to manually compare and update updatedPost.Tags collection with insertedTags.
First of all, I think it should work just fine with only one call to _db.SaveChanges();.
You get an Exception because of this line, just remove it:
entry.Property(e => e.Tags).IsModified = true;
Also, I would put
_db.Posts.Attach(updatedPost);
before updatedPost.Tags = insertedTags;.
After these changes, your code most likely should work.
Another option is to use ChangeRelationshipState() method. Take a look at it in the code below. You can also check out this EF Relationships and Navigation Properties doc for more info (see "Creating and Modifying Relationships" section).
I changed only second part of your code and also added "using":
using System.Data.Entity.Infrastructure;
var tags = TagConverter.FetchTags(postText);
var newTags = tags.Select(t => t.Title);
var savedTags = _db.Tags.Where(t => newTags.Contains(t.Title)).ToList();
var notSavedTags = tags.Where(t => savedTags.All(st => st.Title != t.Title)).ToList();
var insertedTags = _db.Tags.AddRange(notSavedTags).ToList();
insertedTags.AddRange(savedTags);
_db.SaveChanges();
var updatedPost = post();
_db.Posts.Attach(updatedPost);
updatedPost.Title = postText;
var entry = _db.Entry(updatedPost);
entry.Property(e => e.Title).IsModified = true;
foreach (var tag in insertedTags)
{
((IObjectContextAdapter)DbContext).ObjectContext.ObjectStateManager.
ChangeRelationshipState(updatedPost, tag, e => e.Tags, EntityState.Added);
}
_db.SaveChanges();
I need to update a second table in my EF, this table is my storage of purchase, i call this entity and search the value for change (Qty), i do the math comparasion and send back the data updated, but raise a error tell me "An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key"
How fix the error? Thanks
My Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(PurchaseDetails purchasedetails)
{
ViewBag.PurchaseID = new SelectList(db.Purchases, "PurchaseID", "Notes", purchasedetails.PurchaseID);
ViewBag.idArt = new SelectList(db.Art, "idArt", "des_art", purchasedetails.IdArt);
ViewBag.idAlmacen = new SelectList(db.Almacens, "idAlmacen", "des_alma", purchasedetails.IdAlmacen);
var cant_details = db.PurchaseDetails.Where(p => p.PurchaseDetailsID == purchasedetails.PurchaseDetailsID).FirstOrDefault();
var cantidad = purchasedetails.Qty - cant_details.Qty;
if (ModelState.IsValid)
{
db.Entry(purchasedetails).State = EntityState.Modified;
db.SaveChanges();
var stock_id = db.Stock.Where(s => s.idAlmacen == purchasedetails.IdAlmacen && s.idArt == purchasedetails.IdArt).FirstOrDefault();
stock_id.stcActual = stock_id.stcActual + cantidad;
db.Stock.Attach(stock_id);
var entry = db.Entry(stock_id);
entry.Property(e => e.stcActual).IsModified = true;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(purchasedetails);
}
I found The solution in this post
How do I detach objects in Entity Framework Code First?
i used AsNoTracking() in my line code
var cant_details = db.PurchaseDetails.Where(p => p.PurchaseDetailsID == purchasedetails.PurchaseDetailsID).FirstOrDefault();
and work perfectly
var cant_details = db.PurchaseDetails.AsNoTracking().Where(p => p.PurchaseDetailsID == purchasedetails.PurchaseDetailsID).FirstOrDefault();
i'm sorry if this question has already been asked, but i'm in trouble with my method of updating collection in Entity Framework.
Let me explain the situation :
- I have for example one model CUSTOMER with some properties and a collection of ORDERS (for example).
- Let's imagine we have an admin page on wich we can edit all the ORDERS for a customer, and when we submit the form, it will send us back the object CUSTOMERS with updated ORDERS (some added, some updated and some deleted).
For the moment i use something like this in order to compare old collection and new collection and determine which object i need to delete/update/add
var toRemove = new List<ORDERS>();
var toAdd = new List<ORDERS>();
foreach (
var order in
oldList.Where(
order =>
newList.FirstOrDefault(t => t.link_id == order.link_id) == null))
{
toRemove.Add(order);
}
foreach (
var order in
newList.Where(
order =>
oldList.FirstOrDefault(t => t.link_id == order.link_id) == null))
{
toAdd.Add(order);
}
foreach (var ORDERSe in toRemove)
{
bdd.ORDERS.Remove(ORDERSe);
}
foreach (var ORDERSe in toAdd)
{
ORDERSe.pjt_id = project_id;
bdd.ORDERS.Add(ORDERSe);
}
foreach (
var order in
newList.Where(
order =>
oldList.FirstOrDefault(t => t.link_id == order.link_id) != null))
{
var child = oldList.FirstOrDefault(t => t.link_id == order.link_id);
bdd.Entry(child).CurrentValues.SetValues(order);
}
But i'm unconfortable with this, because in my mind, entity framework should be able to do the work for me !
I was hoping something like :
customer.orders = newOrders;
Did i missed anything about entity framework or ?
Because when i do this, it just duplicate my orders.
Thanks in advance for your answer.
You can certainly make it cleaner using .Except() and .Intersect(), but the concept doesn't really change, AFAIK you still have to individually remove, update & add the entries in loops...
var oldList = new List<ORDERS>();
var newList= new List<ORDERS>();
var IdsToRemove = oldList.Select(t => t.link_id).Except(newList.Select(t => t.link_id));
var IdsToAdd = newList.Select(t => t.link_id).Except(oldList.Select(t => t.link_id));
var IdsToUpdate = newList.Select(t => t.link_id).Intersect(oldList.Select(t => t.link_id));
//remove
bdd.orders.where(x => IdsToRemove.Contains(x.link_id)).ForEach(x => bdd.Remove(x));
//add
foreach(var order in newList.Where(x -> IdsToAdd.Contains(x.link_id))
{
bdd.Orders.Attach(order);
bdd.Entries(order).EntityState = EntityState.Added;
}
//update
foreach(var order in newList.Where(x -> IdsToUpdate .Contains(x.link_id))
{
bdd.Orders.Attach(order);
bdd.Entries(order).EntityState = EntityState.Modified;
}
bdd.SaveChanges();
But i'm unconfortable with this, because in my mind, entity framework
should be able to do the work for me !
In fact, EF does the Work for you. Using the data context SaveChanges method EF should be able to save all your changes at once:
DbContext.SaveChanges()
For your convinience you can still override this method. Internally you should use something like this:
public override int SaveChanges()
{
var changeSet = ChangeTracker.Entries<IAuditable>();
if (changeSet != null)
{
foreach (var entry in changeSet.Where(c => c.State != EntityState.Unchanged))
{
entry.Entity.ModifiedDate = DateProvider.GetCurrentDate();
entry.Entity.ModifiedBy = UserName;
}
}
return base.SaveChanges();
}
I'm using the Ado.net Entity Framework for the first time and I need to check if this record exist before I insert it to the database. Preferably I'd search if AuthodSSID exists and not the key (AuthorID). I'm using VS2010, Framework 4. System.Data.Entity is 3.5.0.0.
I googled, but found no answer for this question.
PublishingCompanyEntities publishContext;
publishContext = new PublishingCompanyEntities();
private void createNew_Click(object sender, EventArgs e)
{
Author newAuthor = new Author();
newAuthor.FirstName = firstName.Text;
newAuthor.LastName = lastName.Text;
newAuthor.AuthodSSID = 20;
newAuthor.AuthorID = 10
//Check if record exist here
publishContext.AddToAuthor(newAuthor);//insert if does not exist
}
The only way to check if a record exists is to query the record and see if anything comes back:
var existingAuthorCount = publishContext.Author.Count(a => a.AuthodSSID == 20);
if (existingAuthorCount == 0)
{
// Do your insert
}
Something like this should work:
if (publishContext.Author.Select(a => a.AuthodSSID).Where(id => id == 20).Take(1) == null)
// It doesn't exist
else
// It does exist
Based on my (albeit fundamental) understanding this should produce a SQL statement equivalent to:
SELECT TOP(1) AutodSSID FROM Author WHERE AuthodSSID = 20;
Another simpler approach may be to use the Any extension method:
if (!publishContext.Author.Any(a => a.AuthodSSID == 20))
// Put your insert logic here.
I personally prefer this approach from a .NET point of view. It is cleaner and if you care about speed (in .NET), it is more efficient, however the SQL is not that flash;
private bool CheckIfEntityRecordExists(Entity e)
{
var retVal = false;
using (var db = new EntityContext())
{
retVal = db.AdviserClients.Any(a => a.Id == e.Id);
}
return retVal;
}
So for a efficient SQL statement, the following is the best:
private bool CheckIfEntityRecordExists(Entity e)
{
var retVal = false;
using (var db = new EntityContext())
{
retVal = db.AdviserClients.Count(a => a.Id == e.Id) > 0;
}
return retVal;
}
This method is advised to only be used for migrations to seed data, not as an upsert method
There is so called "upsert" operation available in EF v5.0+
publishContext.Author.AddOrUpdate(x => x.Id, newAuthor)
AddOrUpdate can be found in the "System.Data.Entity.Migrations" namespace, so don't forget to add:
using System.Data.Entity.Migrations;
The AddOrUpdate operation is not atomic. But *if (existingAuthorCount == 0) {// Do your insert} isn't also.
All you need to do is search (with linq) for an author with that ID.
The Where() method will return a collection of authors you only need one, so you use FirstOrDefault() which returns the first element or null if there is nothing. You could also use SinglOrDefault which throws an exception if there is more than one element in the list, or just returns that element.
Seems #Jacob has an excellent, more efficient approach!
var author = publishContext.Authors.Where
(a=>a.AuthodSSID == 10).FirstOrDefault();
if(author == null) //none exist
{//don't bother creating one unless you need to..
Author newAuthor = new Author();
newAuthor.FirstName = firstName.Text;
newAuthor.LastName = lastName.Text;
newAuthor.AuthodSSID = 20;
newAuthor.AuthorID = 10
publishContext.AddToAuthor(newAuthor);//insert if does not exist
}