Update a Second Table or Model in EF - MVC - c#

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();

Related

db.Entry().CurrentValues.SetValues() remove my existing raw in Entity Framework C#

When i try to update table with currentvalues.setvalues() it is removed my existing row in ef in c# .net
var oldItem = db.ClaimDetail.FirstOrDefault(w => w.id == objParam.id );
if (oldItem != null)
{
newItem.id = oldItem.id;
db.Entry(oldItem).CurrentValues.SetValues(newItem);
db.SaveChanges();
}
else
{
MyEntity.Add(newItem);
}
It should update raw not removed it.
Istead of this
db.Entry(oldItem).CurrentValues.SetValues(newItem);
use this
db.Entry(newItem).State = EntityState.Modified;

Add data to View from additional Table

I would like to call a Create View from the "Buchungen" controller.
To do this, I would like to add some data from another table (ArWoo) to the view so that it is already filled out in advance.
The two tables are not linked.
I give the appropriate ID when calling the "Buchungen" controller.
// GET: Buchungen/Create_AR
public IActionResult Create_AR(int? id)
{
var AR = _context.ArWoo
.Where(n => n.Id == id);
ViewData["Bestellnummer"] = AR.Bestellnummer;
return View("Create");
}
How can I now transfer a value from AR (e.g. "Bestellnummer") to the view?
I thought that would just go along with ViewData["Bestellnummer"] = AR.Bestellnummer;
But this doesn´t work.
If I set a breakpoint at return View ("Create"), I see that the variable AR is correctly assigned.enter image description here
Fix the action by adding FirstOrdefault to a query
public IActionResult Create_AR(int? id)
{
var AR = _context.ArWoo
.Where(n => n.Id == id).FirstOrDefault();
ViewData["Bestellnummer"] = AR.Bestellnummer;
// or return View("Create",AR.Bestellnummer);
return View("Create");
}

EF Core - Modify realations without loading entity

I was trying to implement a function that will let a user like a comment. If the user has already liked it, it can't be liked again and vice versa.
This is what it looks like:
public async Task<ActionResult<CommentResponse>> LikeComment(LikeComment like)
{
if (like.HasNullProperty())
return BadRequest("Missing properties!");
var comment = await commentService.GetCommentWithLikes((int) like.CommentId);
if(comment is null)
return NotFound($"No comment with id {like.CommentId} was found");
try
{
var userId = User.GetUserID();
comment = await commentService.LikeComment(comment, userId, (bool)like.Liked);
return comment is not null ? Ok(comment.GetCommentResponse((bool)like.Liked)) : StatusCode(304);
}
catch(Exception e)
{
return StatusCode(500, $"Error while trying to {((bool)like.Liked ? "like" : "dislike")} comment");
}
}
Relevant functions:
public async Task<Comment> GetCommentWithLikes(int id) => await blogContext.Comments.IncludeLikes().FirstOrDefaultAsync(x => x.Id == id);
public static IQueryable<Comment> IncludeLikes(this IQueryable<Comment> source)
=> source.Select(x => new Comment
{
Id = x.Id,
ArticleId = x.ArticleId,
CreatedById = x.CreatedById,
CreatedAt = x.CreatedAt,
Likes = x.LikedBy.Count,
Text = x.Text,
});
And the main like logic:
public async Task<Comment> LikeComment(Comment comment, string userId, bool liked)
{
var user = new User { Id = userId };
var hasLiked = await blogContext.Comments.Where(x => x.Id == comment.Id && x.LikedBy.Any(x => x.Id == user.Id)).FirstOrDefaultAsync() is not null;
Action action = null;
if (!hasLiked && liked)
{
action = () => comment.LikedBy.Add(user);
comment.LikedBy = new List<User>();
comment.Likes++;
}
else if (hasLiked && !liked)
{
action = () => comment.LikedBy.Remove(user);
comment.LikedBy = new List<User> { user };
comment.Likes--;
}
if (action is null)
return null;
blogContext.Attach(user);
blogContext.Attach(comment);
action();
await blogContext.SaveChangesAsync();
return comment;
}
The idea was to not load the whole likedBy relation, but still notify EF Core that i have added or removed one user. Therefore i modify the Comment, then attach it so EF Core tracks the changes to the likedBy relation. Interestingly, it works fine when liking a comment. However, when disliking, i get an rrror that the comment is already attached. Using .AsNoTracking() in the GetCommentsWithLikes function didn't help.
The instance of entity type 'Comment' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.
This is the comment passed to the like func when linking (works):
This is the one when disliking (only diff is the like count...):
And this is it right before the failing attach:
Maybe someone knows the reason for this behaviour and can help me or suggest a different approach :)
Thanks
Using .AsNoTracking() in the GetCommentsWithLikes function didn't help
Due to the used projection, that function is already implicitly no tracking. It is the following call
var hasLiked = await blogContext.Comments
.Where(x => x.Id == comment.Id && x.LikedBy.Any(x => x.Id == user.Id))
.FirstOrDefaultAsync() is not null;
which is adding a Comment instance to the change tracker when the result is not null.
Since you don't need that instance and are just checking for existence, use the following instead which doesn't involve entity instances, but pure server side query:
var hasLiked = await blogContext.Comments
.AnyAsync(x => x.Id == comment.Id && x.LikedBy.Any(x => x.Id == user.Id));

Update A Table From A List

I have two tables. One of them is Customers which is in the database, the other one is ChangedCustomers, which comes from the user. I write the updated model, I guess there is something missing.
public async Task<int> UpdateCustomers (IENumerable<ChangedCustomers> changedCustomers
{
foreach(var item in changedCustomers)
{
_context.Customers.Updaate (new Customers()
{
CustomerName=item.CustomerName,
CustomerAddress=item.CustomerAddress
});
}
return await _context.SaveChangesAsync();
}
In Method I am not saying something like "update that row when Id values in Customers and ChangedCustomers tables are equal". I need this but I can't. How can I do it?
You are trying update the row by adding a new row? Thats what it looks like. If you want to update a specific row, you will need to fetch that row, then update the values.
foreach (var item in changedCustomers)
{
var customer = _context.Customers
.FirstOrDefault(x => x.CustomerId == item.CustomerId);
if (customer != null)
{
customer.CustomerName = item.CustomerName;
customer.CustomerAddress = item.CustomerAddress;
}
else
{
customer = new Customer
{
CustomerName = item.CustomerName,
CustomerAddress = item.CustomerAddress
}
_context.Add(customer);
}
}
_context.SaveChangesAsync();
FirstOrDefault() will retrieve the first value from _context.Customers that matches the expression, if it doesn't find any it will default to null. If it is not null then you can make the changes, if it is null, you could add a new value.
Here's general logic that you'd follow to update an existing record which could be placed within your loop:
//Find the entity already tracked based on table key
var entity = context.Customers.FirstOrDefault(item => item.YourTableID == id);
// Validate entity is not null
if (entity != null)
{
// Make changes to specific field
entity.Name = "Me";
// Update entity in it's entirety
entity = new Customers() { //Your logic here to build the updated entity }
// Save changes in database
context.SaveChanges();
}
Just mark the entities as modified and call SaveChanges.
Only UPDATE sql queries will be executed.
The code in #jaabh answer is very inefficient, since it pre-executes sql-queries SELECT, reading from the database those entities that we already have. This is unnecessary.
public async Task<int> UpdateCustomers(IEnumerable<ChangedCustomers> changedCustomers)
{
foreach (var item in changedCustomers)
{
_context.Entry(item).State = EntityState.Modified;
}
return await _context.SaveChangesAsync();
}

Updating certain properties of entity using EF6 without loading entire entity

(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.

Categories

Resources