I have this ef method where I am using this Include and select the parent table using the Single Id for MeetingPollingId. I am sending a model to be created into the database. Its working fine for new record where I send a 0 for the MeetingPollingQuestionId but when I send a value where there is already a record I wanted it to update the data and not create a new record. Is there something else I need to change for the update to working?
public ResultStatus SaveMeetingPollingQuestion(Model.MeetingPollingQuestion mpq)
{
using (var db = new NccnEcommerceEntities())
{
using (DbContextTransaction dbTran = db.Database.BeginTransaction())
{
ResultStatus result = new ResultStatus();
try
{
var meetingPolling = db.MeetingPollings
.Include(x => x.MeetingPollingQuestions)
.Include(x => x.MeetingPollingQuestions.Select(q => q.MeetingPollingParts))
.Single(x => x.MeetingPollingId == mpq.MeetingPollingId);
var newMeetingPollingQuestion = new EFModel.MeetingPollingQuestion
{
MeetingPollingQuestionId = mpq.MeetingPollingQuestionId,
MeetingPollingId = mpq.MeetingPollingId,
MeetingPollingQuestionTypeId = mpq.MeetingPollingQuestionTypeId,
SequenceOrder = mpq.SequenceOrder,
MeetingPollingParts = mpq.MeetingPollingParts.Select(p => new EFModel.MeetingPollingPart
{
MeetingPollingPartsTypeId = p.MeetingPollingPartsTypeId,
MeetingPollingPartsValues = p.MeetingPollingPartsValues.Select(v => new EFModel.MeetingPollingPartsValue
{
QuestionValue = v.QuestionValue,
MeetingPollingPartsValuesTypeId = v.MeetingPollingPartsValuesTypeId
}).ToList()
}).ToList()
};
meetingPolling.MeetingPollingQuestions.Add(newMeetingPollingQuestion);
db.SaveChanges();
dbTran.Commit();
result.ResultCode = Convert.ToInt32(Enums.Result.Code.Success);
result.Message = "Successfully uploaded.";
}
catch (Exception e)
{
dbTran.Rollback();
result.Message = "Error Save Meeting Polling Question" + e.Message;
result.ResultCode = Convert.ToInt32(Enums.Result.Code.Error);
}
return result;
}
}
}
What you are asking for is an "Upsert" which is not supported "out of the box" by Entity Framework. Upserts are supported in other query languages like PostgreSQL, etc.
You will have to "roll your own" Upsert functionality using Entity Framework. Since you are using .Add() in your example, you are only Insert-ing data, but never updating it. To do this, you will need to check if the question id already exists.
You can do something similar to this...
// determine whether the question id exists
var mpqIdExists = db.MeetingPollings.AsNoTracking().Where(mp => mp.MeetingPollingId == mpq.MeetingPollingId && mp.MeetingPollingQuestionId == mpq.MeetingPollingQuestionId).Any();
if (mpqIdExists)
{
// update
db.Entry(meetingPolling.MeetingPollingQuestions).CurrentValues.SetValues(newMeetingPollingQuestion);
}
else
{
// insert
meetingPolling.MeetingPollingQuestions.Add(newMeetingPollingQuestion);
}
db.SaveChanges();
dbTran.Commit();
Courtesy of:
More efficient way to perform a UPSERT with EF6
Related
I'm trying to get a upsert working with Entity Framework. I have the following code that is throwing an error:
using (var db = new Entities.DB.DConn())
{
//...
foreach (Account account in accounts)
{
Entities.DB.Account dlAccount = new Entities.DB.Account();
dlAccount.GId = dlG.Id;
dlAccount.AccountName = account.NameAtFI;
dlAccount.AccountNumber = account.AcctNumber;
dlAccount.AcctType = account.AcctType;
dlAccount.AsOfDate = account.DateCreated;
dlAccount.IsDeleted = false;
dlAccount.DateModified = DateTime.UtcNow.ToUniversalTime();
db.Entry(dlAccount).State = ((dlAccount.GId == dlG.Id) ? EntityState.Modified : EntityState.Added);
db.SaveChanges();
}
}
Exception:
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded.
Essentially all I want to do is update the record if dlAccount.GId == dlG.Id or insert it if it does not exist. The following code achieves what i want without using EntityState:
using (var db = new Entities.DB.DConn())
{
//...
foreach (Account account in accounts)
{
bool isNewRecord = false;
Entities.DB.Account dlAccount = new Entities.DB.Account();
Entities.DB.Account exisitngAcct = db.Accounts.Where(x => x.GId == dlG.Id).FirstOrDefault(); //x.GId is NOT ad primary key
if (exisitngAcct != null)
{
dlAccount = exisitngAcct;
isNewRecord = true;
}
dlAccount.GId = dlG.Id;
dlAccount.AccountName = account.NameAtFI;
dlAccount.AccountNumber = account.AcctNumber;
dlAccount.AcctType = account.AcctType;
dlAccount.AsOfDate = account.DateCreated;
dlAccount.IsDeleted = false;
dlAccount.DateModified = DateTime.UtcNow.ToUniversalTime();
if (isNewRecord)
{
dldb.Accounts.Add(dlAccount);
}
db.SaveChanges();
}
}
Can anyone see anything that I'm perhaps doing wrong here? I would really like to get this working and avoid having to use over bloated code like above.
TIA
First I should point out that the logic in the (non-EntityState) example you posted doesnt look like I would expect it to - at least based on my understanding, which may be wrong :)
*Disclaimer - I've hacked this out in a text editor, please excuse any bugs.
If we take this as your requirement:
Essentially all I want to do is update the record if dlAccount.GId == dlG.Id or insert it if it does not exist.
Then I would expect the non-EntityState version to look like this:
using (var db = new Entities.DB.DConn())
{
//...
foreach (Account account in accounts)
{
Entities.DB.Account dlAccount = null;
Entities.DB.Account exisitngAcct = db.Accounts.Where(x => x.GId == dlG.Id).FirstOrDefault(); //x.GId is NOT ad primary key
if (exisitngAcct != null)
{
//If there is an EXISTING account, it will already be tracked by EF so no need to attach it.
dlAccount = exisitngAcct;
}
else
{
//No account exists, so we need to create one, and ADD it to our EF context as a new Entity
dlAccount = new Entities.DB.Account();
db.Accounts.Add(dlAccount);
}
dlAccount.GId = dlG.Id;
dlAccount.AccountName = account.NameAtFI;
dlAccount.AccountNumber = account.AcctNumber;
dlAccount.AcctType = account.AcctType;
dlAccount.AsOfDate = account.DateCreated;
dlAccount.IsDeleted = false;
dlAccount.DateModified = DateTime.UtcNow.ToUniversalTime();
db.SaveChanges();
}
}
Assuming the above is what you need, and also assuming that we have a good reason not to just use EF tracking as above, then manually handling EF state would look something like this:
using (var db = new Entities.DB.DConn())
{
//...
foreach (Account account in accounts)
{
Entities.DB.Account exisitngAcct = db.Accounts.FirstOrDefault(x => x.GId == dlG.Id).FirstOrDefault(); //x.GId is NOT ad primary key
//NB. Since we're already pulling up the record with EF, there is *probably* no measurable advantage in not just using EF tracking at this point (unless this is a HUUUGE list of objects)
// in which case we should use the .AsNoTracking() modifier when we load the records (and they should be loaded in batches/all at once, to reduce DB hits)
Entities.DB.Account dlAccount = new Entities.DB.Account();
if(exisitngAcct == null)
{
db.Entry(dlAccount).State = EntityState.Added;
}
else
{
dlAccount.Id = exisitngAcct.Id; //We have to set the PK, so that EF knows which object to update
db.Entry(dlAccount).State = EntityState.Modified;
}
dlAccount.GId = dlG.Id;
dlAccount.AccountName = account.NameAtFI;
dlAccount.AccountNumber = account.AcctNumber;
dlAccount.AcctType = account.AcctType;
dlAccount.AsOfDate = account.DateCreated;
dlAccount.IsDeleted = false;
dlAccount.DateModified = DateTime.UtcNow.ToUniversalTime();
db.SaveChanges();
}
}
I have the following code:
public int DeActivate(User entity) {
try {
using (UsersDataContext usersDC = new UsersDataContext()) {
users user = new users();
user = usersDC.users.Where(x => x.id == entity.Id).
Select(x => new users {active = x.active}).FirstOrDefault();
//user.active = entity.Active;
user.active = false;
usersDC.SubmitChanges();
return 1;
}
}
catch {
return 0;
}
}
While running an NUnit test on the method, the method returns 1, as it is supposed to do, and while de-bugging no exceptions are thrown. But, when i cross check with the DB the records have not being affected. I have tried the following: Re-created DBML file, checked for existance of PK, and checked the following sites:
MSDN question, StackOverflow question, but to no avail.
Your select statement is wrong. Try this.
public int DeActivate(User entity) {
try {
using (UsersDataContext usersDC = new UsersDataContext()) {
var user = usersDC.users.Single(x => x.id == entity.Id);
user.active = false;
usersDC.SubmitChanges();
return 1;
}
} catch {
return 0;
}
}
Here i have a method in ASP.NET MVC. What i am doing is to update single column checking every column of table is not null. If null then IsModified property changing to false. I have to write statement for every column.
My Sample Method -
public int ServicesEdit(helplineservice _helplineservice)
{
int result = 0;
try
{
db.Entry(_helplineservice).State = EntityState.Modified;
if (string.IsNullOrEmpty(_helplineservice.description))
{
db.Entry(_helplineservice).Property(p => p.description).IsModified = false;
}
else
{
db.Entry(_helplineservice).Property(p => p.description).IsModified = true;
}
if (string.IsNullOrEmpty(_helplineservice.title))
{
db.Entry(_helplineservice).Property(p => p.title).IsModified = false;
}
else
{
db.Entry(_helplineservice).Property(p => p.title).IsModified = true;
}
if (string.IsNullOrEmpty(_helplineservice.contactnumber))
{
db.Entry(_helplineservice).Property(p => p.contactnumber).IsModified = false;
}
else
{
db.Entry(_helplineservice).Property(p => p.contactnumber).IsModified = true;
}
//if (string.IsNullOrEmpty(_helplineservice.active.ToString()))
//{
// db.Entry(_helplineservice).Property(p => p.active).IsModified = false;
//}
//else
//{
// db.Entry(_helplineservice).Property(p => p.active).IsModified = true;
//}
db.SaveChanges();
result = 1;
}
catch (Exception ex)
{
result = 0;
}
return result;
}
Calling Above Method -
helplineservice _helplineservice = new helplineservice();
_helplineservice.helplineid =sectionid;
_helplineservice.allowedtoapp = allow;
result = _ftwCommonMethods.ServicesEdit(_helplineservice);
This code not look logical i think. Tell me better way to do this. How Can i Update Single Column of Table by not writing this much code? Thanks in Advance.
You can avoid all of the checking by loading the entity you want to update first.
var context = new DbContext();
// Load entity via whatever Id parameter you have.
var entityToUpdate = context.Set<Type>().FirstOrDefault(x => x.Id == idToUpdate);
if(entityToUpdate != null)
{
entityToUpdate.Value1 = newValue1;
entityToUpdate.Value2 = newValue2;
context.SaveChanges();
}
Only Value1 and Value2 will be updated. All other existing values will remain unchanged.
In your case, what you are doing is creating a new empty entity, then setting its Key to something that already exists in the database. When you attach that entity to the context, EF has no choice but to assume that all the values in that entity are the new updated values. This is standard behavior for EF.
Using EF6.1, I'm working with a table with a composite key as below:
How can I run an upsert method using Entity Framework?
I've written the following method but I've read that it should not be used for upserts, only migrations (please disregard the design pattern for now):
public void UpsertNumberOfMarkets(List<Entities.NumberOfMarkets> marketsList)
{
using (MyDbContext db = new MyDbContext())
{
foreach (var market in marketsList)
{
db.NumberOfMarkets.AddOrUpdate(market);
}
}
}
I'm also not certain that it works correctly. Any thoughts? I'd like to avoid deleting and inserting as we have an audit logging table for updates.
EDIT: I've written the following method which may handle this - is this the preferred approach?
public void UpsertNumberOfMarkets(List<Entities.NumberOfMarkets> marketsList)
{
using (MyDbContext db = new MyDbContext())
{
foreach (var market in marketsList)
{
var predicate = PredicateBuilder.True<Entities.NumberOfMarkets>();
predicate = predicate.And(n => n.ProjectId == market.ProjectId);
predicate = predicate.And(n => n.Year == market.Year);
var existingMarketEntry = db.NumberOfMarkets.AsExpandable().Where(predicate).FirstOrDefault();
if (existingMarketEntry != null)
existingMarketEntry.Markets = market.Markets;
else
{
db.NumberOfMarkets.Add(market);
}
}
db.SaveChanges();
}
}
Yes.AddOrUpdate should only be used for migrations for below reason
It Updates all the values which are provided but mark all other values as NULL (which are not provided) which may not be the behaviour we want in real world application.
In your case you can follow below steps
using (MyDbContext db = new MyDbContext())
{
foreach (var market in marketsList)
{
var existingMarket =
db.Markets.FirstOrDefault(x => x.ProjectID == market.ProjectID && x.Year == market.Year);
if (existingMarket != null)
{
//Set properties for existing market
existingMarket.Year == market.Year
//etc
}
else
{
db.Markets.Add(market);
}
db.SaveChanges();
}
}
I want to upsert reference members of an existing entity.
Do I have to write specific code for the upsert?
meaning: I have to check if I'm handling an existing reference member or a new one.
Is there any other simple way to do so?
What happens when you do only Save ?
public void SaveCofiguration(MamConfiguration_V1Ui itemUi)
{
var itemEf = mMamConfiguration_V1UiToEfConvertor.ConvertToNewEf(itemUi);
using (var maMDBEntities = new MaMDBEntities())
{
IDal<MamConfiguration_V1> mamConfigurationDal = mDalFactory.GetDal<MamConfiguration_V1>(maMDBEntities);
mamConfigurationDal.Save(itemEf);
}
}
public MamConfiguration_V1 GetById(object id)
{
id.ThrowIfNull("id");
int configurationId = Convert.ToInt32(id);
var result =
mMaMDBEntities.MamConfiguration_V1.SingleOrDefault(item => item.ConfigurationId == configurationId);
return result;
}
public MamConfiguration_V1 Save(MamConfiguration_V1 item)
{
item.ThrowIfNull("item");
var itemFromDB = GetById(item.ConfigurationId);
if (itemFromDB != null)
{
UpdateEfItem(itemFromDB, item);
// if (mMaMDBEntities.ObjectStateManager.GetObjectStateEntry(itemFromDB).State == EntityState.Detached)
// {
// mMaMDBEntities.MamConfiguration_V1.AddObject(itemFromDB);
// }
// Attached object tracks modifications automatically
mMaMDBEntities.SaveChanges();
return item;
}
private void UpdateEfItem(MamConfiguration_V1 itemFromDb, MamConfiguration_V1 itemFromUi)
{
itemFromDb.UpdatedDate = DateTime.Now;
itemFromDb.Description = itemFromUi.Description;
itemFromDb.StatusId = itemFromUi.StatusId;
itemFromDb.Name = itemFromUi.Name;
itemFromDb.NumericTraffic = itemFromUi.NumericTraffic;
itemFromDb.PercentageTraffic = itemFromUi.PercentageTraffic;
itemFromDb.Type = itemFromUi.NumericTraffic;
foreach (var item in itemFromDb.MamConfigurationToBrowser_V1.ToList())
{
if (itemFromUi.MamConfigurationToBrowser_V1.All(b => b.BrowserVersionId != item.BrowserVersionId))
{
mMaMDBEntities.MamConfigurationToBrowser_V1.DeleteObject(item);
}
}
for (int i = 0; i < itemFromUi.MamConfigurationToBrowser_V1.Count; i++)
{
var element = itemFromUi.MamConfigurationToBrowser_V1.ElementAt(i);
var item = itemFromDb.MamConfigurationToBrowser_V1.SingleOrDefault(b => b.BrowserVersionId == element.BrowserVersionId);
if (item != null)
{
// copy properties from element to item
}
else
{
element.Browser = mMaMDBEntities.Browsers.Single(browserItem =>
browserItem.BrowserID == element.BrowserID);
//element.MamConfiguration_V1 = itemFromDb;
//have also tried: element.MamConfiguration_V1 = null;
//element.MamConfiguration_V1Reference = null;
itemFromDb.MamConfigurationToBrowser_V1.Add(element);
}
}
}
But I would have expecte Save(itemUi) and SaveChanges() to work fine. No?
public void InsertOrUpdate(DbContext context, UEntity entity)
{
context.Entry(entity).State = entity.Id == 0 ?
EntityState.Added :
EntityState.Modified;
context.SaveChanges();
}
http://forums.asp.net/t/1889944.aspx/1
To avoid the overhead of a query and then insert, or throwing exceptions, you can take advantage of the underlying database support for merges or upserts.
This nuget package does the job pretty well: https://www.nuget.org/packages/FlexLabs.EntityFrameworkCore.Upsert/
Github: https://github.com/artiomchi/FlexLabs.Upsert
Example:
DataContext.DailyVisits
.Upsert(new DailyVisit
{
// new entity path
UserID = userID,
Date = DateTime.UtcNow.Date,
Visits = 1,
})
// duplicate checking fields
.On(v => new { v.UserID, v.Date })
.WhenMatched((old, #new) => new DailyVisit
{
// merge / upsert path
Visits = old.Visits + 1,
})
.RunAsync();
The underlying generated sql does a proper upsert. This command runs right away and does not use change tracking, so that is one limitation.
See 'AddOrUpdate' method of System.Data.Entity.Migrations.
http://msdn.microsoft.com/en-us/library/system.data.entity.migrations.idbsetextensions.addorupdate%28v=vs.103%29.aspx
using System.Data.Entity.Migrations;
public void Save(Person person) {
var db = new MyDbContext();
db.People.AddOrUpdate(person);
db.SaveChanges();
}
"optimistic" approach for simple scenarios (demos)...
dbContext.Find()'s intellisense help tells us that it either retrieves entity by key if already present in current context, or queries the database to get it... then we know if it exists to either add or update. i'm using EFCore v2.2.0.
var existing = _context.Find<InventoryItem>(new object[] {item.ProductId});
if (existing == null) _context.Add(item);
else existing.Quantity = item.Quantity;
_context.SaveChanges();
DbContext.Update Method
For entity types with generated keys if an entity has its primary key value set then it will be tracked in the Modified state. If the primary key value is not set then it will be tracked in the Added state. This helps ensure new entities will be inserted, while existing entities will be updated. An entity is considered to have its primary key value set if the primary key property is set to anything other than the CLR default for the property type.
For entity types without generated keys, the state set is always Modified.
read this article
you can use this sample