Only Update Entity (Parent or children) if Property Values Have Changed with EF - entity-framework

As I build a project with Entity Framework 6 (using EF for the first time), I noticed that when I only Update the relationships of an Entity, EF updates the main Entity too.
I can tell this is happening because I'm using System Versioned tables on Sql Server 2017.
This is a made up scenario, but most of the concept is here.
public async Task<ActionResult> Edit([Bind(Include="Id,Name,LocationTimes")] LocationViewModel locationVM) {
if (ModelState.IsValid) {
var location = await _db.Locations.FirstOrDefaultAsync(l => l.Id == locationsViewModel.Id && l.UserId == UserId);
if (location == null) {
return HttpNotFound();
}
location.Name = locationsViewModel.Name;
// ... other properties
foreach (var day in locationsViewModel.LocationTimes.Days) {
var time = new Time {
Day = day.Day,
OpenTime = day.OpenTime,
CloseTime = day.CloseTime,
};
// Find current Time or keep newly created
time = await time.FindByTimeAsync(time, _db) ?? time;
// Find LocationTime with same day
var locationTime = location.LocationTimes.FirstOrDefault(lt => lt.Time.Day == day.Day);
// If all times are the same, skip (continue)
if (locationTime != null && locationTime.Time.OpenTime == time.OpenTime && locationTime.Time.CloseTime == time.CloseTime)
continue;
if (locationTime != null && (locationTime.Time.OpenTime != time.OpenTime || locationTime.Time.CloseTime != time.CloseTime)) {
// Remove, At least one of the Times do not match
locationTime.Time = time;
_db.Entry(locationTime).State = EntityState.Modified;
} else {
location.LocationTimes.Add(new LocationTime {
Location = location,
Time = time,
});
}
}
_db.Entry(location).State = EntityState.Modified;
await _db.SaveChangesAsync();
return RedirectToAction("Index");
}
}
I assume, that by marking the entire Entity as Modified, EF will call the update statement.
How can I avoid an UPDATE to the parent Entity, if no properties have changed on the parent, but still Add/Update the child relationships?
I assume I have to check that each property has not changed and therefore I should not be setting location state to Modified, but how would I handle the newly added Times?
Update #1
So I tried what I mentioned and it works, but is this the correct way to do this?
public async Task<ActionResult> Edit([Bind(Include="Id,Name,LocationTimes")] LocationViewModel locationVM) {
if (ModelState.IsValid) {
var location = await _db.Locations.FirstOrDefaultAsync(l => l.Id == locationsViewModel.Id && l.UserId == UserId);
if (location == null) {
return HttpNotFound();
}
/*******************
This is new part
*******************/
if (
location.Name != locationsViewModel.Name
// || ... test other properties
) {
location.Name = locationsViewModel.Name;
// ... other properties
_db.Entry(location).State = EntityState.Modified;
} else {
_db.Entry(location).State = EntityState.Unchanged;
}
/*******************/
foreach (var day in locationsViewModel.LocationTimes.Days) {
var time = new Time {
Day = day.Day,
OpenTime = day.OpenTime,
CloseTime = day.CloseTime,
};
// Find current Time or keep newly created
time = await time.FindByTimeAsync(time, _db) ?? time;
// Find LocationTime with same day
var locationTime = location.LocationTimes.FirstOrDefault(lt => lt.Time.Day == day.Day);
// If all times are the same, skip (continue)
if (locationTime != null && locationTime.Time.OpenTime == time.OpenTime && locationTime.Time.CloseTime == time.CloseTime)
continue;
if (locationTime != null && (locationTime.Time.OpenTime != time.OpenTime || locationTime.Time.CloseTime != time.CloseTime)) {
// Remove, At least one of the Times do not match
locationTime.Time = time;
_db.Entry(locationTime).State = EntityState.Modified;
} else {
location.LocationTimes.Add(new LocationTime {
Location = location,
Time = time,
});
}
}
/* removed, added above */
//_db.Entry(location).State = EntityState.Modified;
await _db.SaveChangesAsync();
return RedirectToAction("Index");
}
}

So after trial and error, I guess I misunderstood how EF handles the EntityState. I though if a child was Modified, you had to set the parent as Modified as well.
Gladly, that's not the case and the code below works as desired.
public async Task<ActionResult> Edit([Bind(Include="Id,Name,LocationTimes")] LocationViewModel locationVM) {
if (ModelState.IsValid) {
var location = await _db.Locations.FirstOrDefaultAsync(l => l.Id == locationsViewModel.Id && l.UserId == UserId);
if (location == null) {
return HttpNotFound();
}
/*******************
This is new part
check if at least one property was changed
*******************/
if (
location.Name != locationsViewModel.Name
|| location.Ref != locationsViewModel.Ref
// || ... test other properties
) {
location.Name = locationsViewModel.Name;
location.Ref = locationsViewModel.Ref;
// ... other properties
// Tell EF that the Entity has been modified (probably not needed, but just in case)
_db.Entry(location).State = EntityState.Modified;
} else {
// Tell EF that the Entity has *NOT* been modified
_db.Entry(location).State = EntityState.Unchanged;
}
/*******************/
foreach (var day in locationsViewModel.LocationTimes.Days) {
var time = new Time {
Day = day.Day,
OpenTime = day.OpenTime,
CloseTime = day.CloseTime,
};
// Find current Time or keep newly created
time = await time.FindByTimeAsync(time, _db) ?? time;
// Find LocationTime with same day
var locationTime = location.LocationTimes.FirstOrDefault(lt => lt.Time.Day == day.Day);
// If all times are the same, skip (continue)
if (locationTime != null && locationTime.Time.OpenTime == time.OpenTime && locationTime.Time.CloseTime == time.CloseTime)
continue;
if (locationTime != null && (locationTime.Time.OpenTime != time.OpenTime || locationTime.Time.CloseTime != time.CloseTime)) {
// Remove, At least one of the Times do not match
locationTime.Time = time;
_db.Entry(locationTime).State = EntityState.Modified;
} else {
location.LocationTimes.Add(new LocationTime {
Location = location,
Time = time,
});
}
}
/* removed, added above */
//_db.Entry(location).State = EntityState.Modified;
await _db.SaveChangesAsync();
return RedirectToAction("Index");
}
}

Related

EF Core 3.x: duplicate value

I have the following code:
public class DeviceVerizon
{
public int DeviceId { get; set; }
public string InternalDeviceId { get; set; }
}
and
public class DeviceVerizonMap : IEntityTypeConfiguration<DeviceVerizon>
{
public void Configure(EntityTypeBuilder<DeviceVerizon> builder)
{
builder.ToTable(nameof(DeviceVerizon));
builder.HasKey(d => d.DeviceId);
builder.HasIndex(v => v.InternalDeviceId).IsUnique();
builder.HasOne(o => o.Device)
.WithOne(o => o.VerizonData)
.HasForeignKey<DeviceVerizon>(a => a.DeviceId)
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
;
}
}
so, InternalDeviceId is just unique index.
some code change InternalDeviceId for existing record and add new record with the previous InternalDeviceId.
So, localDevice.VerizonData.InternalDeviceId = 1420531689
I update it for new (remoteDevice.VerizonData.InternalDeviceId = '111111111')
localDevice.VerizonData = remoteDevice.VerizonData;
_context.Devices.Update(localDevice);
then add new record with remoteDevice.VerizonData.InternalDeviceId = 1420531689:
_context.Devices.Add(remoteDevice);
and after it I call:
await _context.SaveChangesAsync();
but I get an exception:
Cannot insert duplicate key row in object 'dbo.DeviceVerizon' with
unique index 'IX_DeviceVerizon_InternalDeviceId'. The duplicate key
value is (1420531689).
The full code:
foreach (var remoteDevice in remoteVerizonDevices)
{
var localDevice = allLocalDevicesWithIMEI.Where(a =>
(string.IsNullOrWhiteSpace(remoteDevice.IMEI)
&& a.VerizonData != null
&& a.VerizonData.InternalDeviceId == remoteDevice.VerizonData.InternalDeviceId
&& a.IMSI == remoteDevice.IMSI && a.ICCID == remoteDevice.ICCID)
|| a.IMEI == remoteDevice.IMEI).FirstOrDefault();
if (localDevice != null) // device with such imei already exists or IMEI is empty on Verizon, but InternalDeviceId is the same and ICCID with IMSI are the same
{
var verizonStatus = remoteDevice.VerizonData.State.ConvertToDeviceStatusFromVerizonStatus();
var existingRequest = _context.VerizonRequests.Where(a => a.DeviceId == localDevice.Id && a.Result == VerizonRequestResult.Pending).FirstOrDefault();
if (verizonStatus == DeviceStatus.Active && remoteDevice.IsVerizonLastActiveMoreThanDays(60))
{
// existing request for this device does not exist, create new
if (existingRequest == null)
{
localDevice.Status = DeviceStatus.PendingSuspend;
localDevice.DateStatusChanging = DateTime.UtcNow;
localDevice.LastStatusChangeSource = DeviceStatusChangeSource.Verizon;
// create verizon request to change status, because device is offline more than 60 days
verizonSuspensionList.Add(localDevice.Id);
}
}
else
{
// Is Verizon status of device is different than locally?
if (localDevice.Status != verizonStatus)
{
// check whether we have active verizon request, if not, then
if (existingRequest == null)
{
// device is suspended on suremdm, we should suspend on Verizon
if (localDevice.Status == DeviceStatus.Suspended
&& verizonStatus == DeviceStatus.Active)
{
if (localDevice.Tablet != null && localDevice.Tablet.TabletGroup.GroupName == SpecialSureMdmGroups.Suspended)
verizonSuspensionList.Add(localDevice.Id);
}
else if (localDevice.Status != DeviceStatus.Suspended && verizonStatus == DeviceStatus.Suspended)
{
// if device is suspended on verizon, we need to suspend on SureMdm
localDevice.Status = DeviceStatus.PendingSuspend;
localDevice.DateStatusChanging = DateTime.UtcNow;
localDevice.LastStatusChangeSource = DeviceStatusChangeSource.Verizon;
if (localDevice.Tablet != null)
{
localDevice.Tablet.SuspensionDate = DateTime.UtcNow;
sureMdmSuspensionList.Add(new Core.Dto.DeviceManagement.SureMdm.SureMdmMoveDeviceToSuspendedGroupDto
{
TabletId = localDevice.Tablet.TabletId,
CurrentGroupId = localDevice.Tablet.TabletGroupId
});
}
}
else
{
// other cases that device on verizon has different status, that locally
VerizonStatusIsDifferentEvent?.Invoke(
remoteDevice: remoteDevice,
verizonStatus: remoteDevice.VerizonData.State.ConvertToDeviceStatusFromVerizonStatus(),
localStatus: localDevice.Status);
}
}
}
}
var changes = DeviceIsChanged(remoteDevice, localDevice);
if (changes != null && changes.Count > 0)
modifiedDevices.Add((localDevice, changes));
var continueToUpdate = true;
// if verizon data does not exist, add it
if (localDevice.VerizonData == null)
{
var existingInternalVerizonDeviceIdRecord = allDeviceVerizons.Where(a => a.InternalDeviceId == remoteDevice.VerizonData.InternalDeviceId).FirstOrDefault();
if (existingInternalVerizonDeviceIdRecord != null)
{
DeviceWithTheSameVerizonDeviceIdAlreadyExistsEvent?.Invoke(remoteDevice: remoteDevice,
localDevice: existingInternalVerizonDeviceIdRecord.Device,
verizonDeviceId: remoteDevice.VerizonData.InternalDeviceId);
continueToUpdate = false;
}
}
if (continueToUpdate)
{
localDevice.VerizonData = remoteDevice.VerizonData;
if (!string.IsNullOrWhiteSpace(remoteDevice.ICCID))
localDevice.ICCID = remoteDevice.ICCID;
localDevice.EID = remoteDevice.EID;
localDevice.ESN = remoteDevice.ESN;
if (!string.IsNullOrWhiteSpace(remoteDevice.IMSI))
localDevice.IMSI = remoteDevice.IMSI;
if (!string.IsNullOrWhiteSpace(remoteDevice.MDN))
localDevice.MDN = remoteDevice.MDN;
localDevice.MEID = remoteDevice.MEID;
localDevice.MIN = remoteDevice.MIN;
localDevice.MSISDN = remoteDevice.MSISDN;
localDevice.SKU = remoteDevice.SKU;
localDevice.SyncVerizon = true;
if (string.IsNullOrWhiteSpace(localDevice.Name))
localDevice.Name = localDevice.IMEI;
if (!localDevice.DeviceModelId.HasValue)
{
var verizonGroup = allVerizonGroups.Where(a => a.VerizonGroupName == remoteDevice.VerizonData.GroupName).FirstOrDefault();
if (!verizonGroup.DeviceModelId.HasValue)
VerizonGroupIsNotLinkedWithDeviceModelEvent?.Invoke(remoteDevice);
else
localDevice.DeviceModelId = verizonGroup.DeviceModelId;
}
_context.Devices.Update(localDevice);
}
}
else
{
// validate by Internal Verizon DeviceId
var existingInternalVerizonDeviceIdRecord = allDeviceVerizons.Where(a => a.InternalDeviceId == remoteDevice.VerizonData.InternalDeviceId).FirstOrDefault();
if (existingInternalVerizonDeviceIdRecord != null)
{
DeviceWithTheSameVerizonDeviceIdAlreadyExistsEvent?.Invoke(remoteDevice: remoteDevice,
localDevice: existingInternalVerizonDeviceIdRecord.Device,
verizonDeviceId: remoteDevice.VerizonData.InternalDeviceId);
}
else
{
// add new record
if (!string.IsNullOrWhiteSpace(remoteDevice.IMEI))
remoteDevice.Name = remoteDevice.IMEI;
else if (!string.IsNullOrWhiteSpace(remoteDevice.VerizonData.PrimaryPlaceOfUseFirstName) || !string.IsNullOrWhiteSpace(remoteDevice.VerizonData.PrimaryPlaceOfUseLastName))
remoteDevice.Name = $"{remoteDevice.VerizonData.PrimaryPlaceOfUseFirstName} {remoteDevice.VerizonData.PrimaryPlaceOfUseLastName}";
else
remoteDevice.Name = Core.Constants.Common.Undefined;
// set device Model
var verizonGroup = allVerizonGroups.Where(a => a.VerizonGroupName == remoteDevice.VerizonData.GroupName).FirstOrDefault();
if (!verizonGroup.DeviceModelId.HasValue)
VerizonGroupIsNotLinkedWithDeviceModelEvent?.Invoke(remoteDevice);
else
remoteDevice.DeviceModelId = verizonGroup.DeviceModelId;
// set device status
remoteDevice.Status = remoteDevice.VerizonData.State.ConvertToDeviceStatusFromVerizonStatus();
if (!string.IsNullOrWhiteSpace(remoteDevice.VerizonData.CarrierName))
{
var verizonCarrier = allVerizonCarriers.Where(a => a.VerizonCarrierName == remoteDevice.VerizonData.CarrierName).FirstOrDefault();
if (verizonCarrier.CarrierId.HasValue)
remoteDevice.CarrierId = verizonCarrier.CarrierId;
else
{
// notify that carrier is not found locally
LocalCarrierIsNotFoundForNewDeviceEvent?.Invoke(remoteDevice);
}
}
remoteDevice.SyncVerizon = true;
_context.Devices.Add(remoteDevice);
newDevices.Add(remoteDevice);
}
}
}
await _context.SaveChangesAsync();
why so? only one record has 1420531689, old was updated for new value (111111111) and it saved in the one transaction... How to do it correctly?

telerik mvc Create CompositeFilterDescriptor and append it to existing datasource request filter

I am having some issues creating a CompositeFilterDescriptor from scratch.
The scenario: We have date filters on the grid, but are showing date time fields.
When Someone filters to a specific date, we show all records for that day.
when someone filters greater than that day, we edit the filterDescriptor for that filter and add one day to it.
when someone filters to less than or equal to that day, we edit the filterdescriptor for that filter and add one day to it.
Here is the problem: when someone selects "not equal to", the solution would be to create a compositeFilterDescriptor containing two filters. One for less than that day, one for greater than that day.
I may ore may not be doing this part properly. I can not figure out how to add the new compositeFilterDescriptor to the existing dataSourceRequest.
Here is what I have:
static public DataSourceRequest FixDateFilters(DataSourceRequest request, string[] optionalFields = null)
{
if (request?.Filters != null)
{
List<string> matchingFields = new List<string> { "utc", "time" };
List<FilterDescriptor> newDescriptors = new List<FilterDescriptor>();
//if any fields specific to a page have been passed in, append them to the list.
if (optionalFields != null)
{
foreach (string field in optionalFields)
matchingFields.Append(field);
}
foreach (string matchingField in matchingFields)
{
List<FilterDescriptor> descriptors = new List<FilterDescriptor>();
FilterDescriptor descriptor1 = FindFirstFilterByMember(request.Filters, matchingField, true, null);
//there could be up to two filters on a matching field. Get the 2nd one if it exists.
FilterDescriptor descriptor2 = FindFirstFilterByMember(request.Filters, matchingField, true, descriptor1);
//turn the matches into a list for iterating
if (descriptor1 != null)
descriptors.Add(descriptor1);
if (descriptor2 != null)
descriptors.Add(descriptor2);
if (descriptors.Count != 0)
{
foreach (FilterDescriptor descriptor in descriptors)
{
DateTime? utcDate = (DateTime?)descriptor?.Value;
if (utcDate.HasValue)
{
utcDate = Core.DateHelper.ToUTC(utcDate.Value, HttpContext.Current); //make sure date ranges are one day ranges in local time
}
if (utcDate != null)
{
if (descriptor.Operator == FilterOperator.IsLessThanOrEqualTo)
{
utcDate = ((DateTime)utcDate).AddDays(1);
descriptor.Value = utcDate;
}
else if (descriptor.Operator == FilterOperator.IsGreaterThan)
{
utcDate = utcDate.Value.AddDays(1);
descriptor.Value = utcDate;
}
else if (descriptor.Operator == FilterOperator.IsEqualTo)
{
descriptor.Operator = FilterOperator.IsGreaterThanOrEqualTo;
newDescriptors.Add(new FilterDescriptor(descriptor.Member, FilterOperator.IsLessThan, utcDate.Value.AddDays(1)));
}
else if (descriptor.Operator == FilterOperator.IsNotEqualTo)
{
CompositeFilterDescriptor cfd = new CompositeFilterDescriptor();
cfd.LogicalOperator = FilterCompositionLogicalOperator.Or;
cfd.FilterDescriptors.Add(new FilterDescriptor(descriptor.Member, FilterOperator.IsLessThan, utcDate.Value;));
cfd.FilterDescriptors.Add(new FilterDescriptor(descriptor.Member, FilterOperator.IsGreaterThan, utcDate.Value.AddDays(1)));
newDescriptors.Add( cfd);
}
}
}
}
}
//equals matches add a new descriptor so add them after the foreach is done so they don't affect the foreach.
foreach (FilterDescriptor newDescriptor in newDescriptors)
{
if (newDescriptor.Member != "") //a blank descriptor has a emptry string for member
{
request.Filters.Add(newDescriptor);
}
}
}
return request;
}
}
static public FilterDescriptor FindFirstFilterByMember(IEnumerable<IFilterDescriptor> filters, string findMember, bool partialMatch = false, FilterDescriptor previous = null)
{
FilterDescriptor ret = null;
foreach (var filter in filters)
{
var descriptor = filter as FilterDescriptor;
if (descriptor != null
&& (descriptor.Member == findMember || (partialMatch == true && descriptor.Member.ToLower().Contains(findMember.ToLower())))
&& (previous == null || previous != descriptor))
{
ret = descriptor;
break;
}
else if (filter is CompositeFilterDescriptor)
{
ret = FindFirstFilterByMember(((CompositeFilterDescriptor)filter).FilterDescriptors, findMember, partialMatch, previous);
if (ret != null)
break;
}
}
return ret;
}
The second function is a recursive one for getting the filters by string.
The problem section is the "isNotEqualTo" comparison. I don't know how to save the composite filter into the existing request. It gives me a casting error.
Ok it turns out all i needed to do was create a new list of CompositeFilterDescriptor type and append that when necessary. The .add function took both. Sigh.
foreach (CompositeFilterDescriptor newCompositeDescriptor in newCompositeDescriptors)
{
request.Filters.Add(newCompositeDescriptor);
}

EF is not saving changes

I really hope someone can help me. I have following controller-action:
public ActionResult Edit(EditViewModel vm)
{
Item item= db.Items.Find(vm.ItemId);
if (ModelState.IsValid)
{
//breakpoint 1 --> everything works fine, if its debugged step by step from here
//if item´s contact is null or different from viewmodel --> override it
if (item.Contact == null || item.Contact.ContactId != vm.ContactId)
item.Contact = db.Contacts.Find(vm.ContactId);
//if the viewmodel have a ContractUnit set and its different from item´s --> override the contractunit
//is the ContractUnit in viewmodel null set item´s null too
if (vm.ContractUnitId.HasValue && ((item.ContractUnit != null && item.ContractUnit.ContractUnitId != vm.ContractUnitId) || item.ContractUnit == null))
item.ContractUnit = db.ContractUnits.Find(vm.ContractUnitId);
else
if (!vm.ContractUnitId.HasValue)
item.ContractUnit = null;
//same as above just for Building
if (vm.BuildingId.HasValue && ((item.Building != null && item.Building.BuildingId != vm.BuildingId) || item.Building == null))
item.Building = db.Buildings.Find(vm.BuildingId);
else
if (!vm.BuildingId.HasValue)
item.Building = null;
//same as above just for EconomicUnit
if (vm.EconomicUnitId.HasValue && ((item.EconomicUnit != null && item.EconomicUnit.EconomicUnitId != vm.EconomicUnitId) || item.EconomicUnit == null))
item.EconomicUnit = db.EconomicUnits.Find(vm.EconomicUnitId);
else
if (!vm.EconomicUnitId.HasValue)
item.EconomicUnit = null;
//breakpoint 2 --> (no changes to item, if its the first breakpoint)
item= vm.GetItem(item); //override non relational data
db.Entry(item).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
...
}
Method GetItem from EditViewModel:
public Item GetItem(Item item)
{
item.DateReported = this.DateReported; //Datetime
item.Description = this.Description; //String
item.FirstDeadline = this.FirstDeadline; //Datetime
item.SecondDeadline = this.SecondDeadline; //Datetime
item.ThirdDeadline = this.ThirdDeadline; //Datetime
item.ReplacementDeadline = this.ReplacementDeadline; //Datetime
return item;
}
The Problem: EF is not saving the changes to item.Building, item.ContractUnit or item.EconomicUnit. But it does when i am debugging it step by step.
There is no error or exception thrown and the viewmodel and model is filled properly.
I really hope someone can help me to solve this...
I removed the relations from the item-model and now only save the ids in a "long?" attribute. Well, its now additional work to get the related data but at least it works.
Try calling
DBSet.Attach(entityToUpdate);
before
db.Entry(item).State = EntityState.Modified;
Maybe this helps.

Entity Framework savechanges() update delete insert

I want my SaveChanges() function to update a record in my database and if the return value is '1' which is coming from my stored procedure then and only then 'delete' command (stored procedure) should not be executed.
Now, the problem is db.SaveChanges() (an instance of ObjectContext) is updating my record successfully but after updating, it executes the delete command. How should I tell my function that not to execute delete command.
using (var db = new PRLAdminEntities())
{
bool isExists = false;
string lastExisting = string.Empty;
string errorString = string.Empty;
db.Connection.Open();
trans = db.Connection.BeginTransaction();
//accounts to be sent back to client
var countriesToSendBack = new List<Polo.Common.Shared.Entities.Country>();
//process each account requiring database update
if (request.CountriesToUpdate != null)
{
foreach (var country in request.CountriesToUpdate)
{
//countriesToSendBack.Remove(country);
var temp = from row in db.Countries where row.Name.ToUpper() == country.Name.ToUpper() select row;
if (temp.Count<Polo.Common.Shared.Entities.Country>() > 0 && country.ChangeTracker.State == ObjectState.Added)
{
countriesToSendBack.Add(country);
db.Countries.ApplyChanges(country);
isExists = true;
lastExisting = country.Name;
errorString += country.Name + ", ";
//db.GetAllCountries();
//break;
continue;
}
if (country.ChangeTracker.State == ObjectState.Deleted)
{
db.DeleteObject(country);
}
//if a change or modification (not a delete)
if (country.ChangeTracker.State != ObjectState.Deleted)
{
//this account should be sent back
if (!countriesToSendBack.Contains((country)))
countriesToSendBack.Add(country);
if (country.Active == false)
{
db.Countries.ApplyCurrentValues(country);
}
}
//apply all changes
db.Countries.ApplyChanges(country);
}
if (isExists)
{
//response.Success = false;
//errorString.Replace(", " + lastExisting + ",", " & " + lastExisting);
//response.FaultMessage = "Duplicate Records";
}
}
//save all changes
int total = db.SaveChanges();
response.Success = true;
foreach (var countryItem in countriesToSendBack)
{
countryItem.Id = (from row in db.Countries where row.Name.ToUpper() == countryItem.Name.ToUpper() select row.Id).FirstOrDefault();
}
trans.Commit();
//refresh the account data which gets timestamp etc
db.Refresh(RefreshMode.StoreWins,countriesToSendBack);
//set the response values
response.Countries = countriesToSendBack;
}
}
Perhaps I misread your question, I do not totally get what you are trying to do.
But why not call SaveChanges() after the change and when all checks are positive perform a remove() and call savechanges() again?
There is no harm is calling SaveChanges() multiple times. It will mirror it's data to your database. If you perform a remove it will try to delete it in your database. That's the nice thing about it.. it does what you tell it to do ;-)

Item's property inside a list changes value from EF source - is there a way to 'detach' it?

I have a list and this list is being populated thru loop. Each loop gets a collection directly from DB. The problem is that when it gets CaseNo(PK) that has the same CaseNo existing in the list and I changed the value of one of the property, the item that has the same CaseNo which is already in the list also changes.
ex. caseNo 1234, proc code = OTH
then I add another item which I get from DB again. but this time I programatically change the proc code, the item in the list above also changes to that value also.
I do not want that to happen because it is not the same Case in the record because a Case can have different type of pro code as its category.
I get that it is changing because it is detecting that it is the same item as the one in the list but I need to add the same case number in the list as a separate item because the proc code is different. Is there a way I can treat it as different entity as the one in the list?
Ex. of data
1.)CaseNO 123
Proc code OTH
2.) CaseNO 123
Proc code OTH
but I need it to be:
1.)CaseNO 123
Proc code DSP
1.)CaseNO 123
Proc code OTH
it goes wrong when this line is executed.
adrCase.ProcCode = "DSP";
then the item in the list (_adrMasterList) if there is any with the same case no, changes its proc code too to "DSP".
here is my code:
private void GenerateReport(string caseCoordinatorID) //LGF 08012011 ADR-59: add case coordinator - add parameter
{
var dispositions = FileMaintenanceBusiness.Instance.GetManyDispositionInfobyKeyword(_selectedProcCode, "ProcCode");
var adrDispos = FileMaintenanceBusiness.Instance.GetAllADRDispositionInfoList();
var calendarActivities = FileMaintenanceBusiness.Instance.GetManyCalendarActivityInfobyKeyword(_selectedProcCode, "ProcCode");
var adrCalActivities = FileMaintenanceBusiness.Instance.GetAllADRCalendars();
var otherActivities = FileMaintenanceBusiness.Instance.GetManyOtherActivities(_selectedProcCode, "ProcCode");
var adrOtherActivities = FileMaintenanceBusiness.Instance.GetAllADROtherActivities();
_adrMasterList.Clear();
if (_selectedProcCode == "ALL")
{
var dispositionALL = FileMaintenanceBusiness.Instance.GetAllDispositionInfoList();
var adrDisposALL = FileMaintenanceBusiness.Instance.GetAllADRDispositionInfoList();
var calendarActivitiesALL = FileMaintenanceBusiness.Instance.GetAllCalendarActivityInfoList();
var adrCalActivitiesALL = FileMaintenanceBusiness.Instance.GetAllADRCalendars();
var otherActivitiesALL = FileMaintenanceBusiness.Instance.GetAllOtherActivities();
var adrOtherActivitiesALL = FileMaintenanceBusiness.Instance.GetAllADROtherActivities();
if (dispositionALL != null && adrDisposALL != null)//dispos
{
foreach (var dispo in dispositionALL)
{
foreach (var adrDispo in adrDisposALL)
{
if (dispo.DispositionID == adrDispo.DispositionID)
{
var adrCase = FileMaintenanceBusiness.Instance.GetADRMasterInfobyKeyword(adrDispo.CaseNo, "CaseNo");
if (adrCase != null)
{
adrCase.ProcCode = "DSP";
adrCase.ProcDateString = dispo.ProcDate.HasValue ? dispo.ProcDate.Value.ToShortDateString() : string.Empty;
//LGF 08012011 ADR-59: add case coordinator - add CaseCoordinatorFilter
//if (!_adrMasterList.Contains(adrCase) && CaseCoordinatorFilter(caseCoordinatorID, adrCase.CaseNo))
// _adrMasterList.Add(adrCase);
if (CaseCoordinatorFilter(caseCoordinatorID, adrCase.CaseNo))
_adrMasterList.Add(adrCase);
}
}
}
}
}
if (calendarActivitiesALL != null && adrCalActivitiesALL != null)//cals
{
foreach (var cal in calendarActivitiesALL)
{
foreach (var adrCal in adrCalActivitiesALL)
{
if (cal.CalendarItemID == adrCal.CalendarItemID)
{
var adrCase = FileMaintenanceBusiness.Instance.GetADRMasterInfobyKeyword(adrCal.CaseNo, "CaseNo");
if (adrCase != null)
{
adrCase.ProcCode = "CAL";
adrCase.ProcDateString = cal.ProcDate.ToShortDateString();
//LGF 08012011 ADR-59: add case coordinator - add CaseCoordinatorFilter
//if (!_adrMasterList.Contains(adrCase) && CaseCoordinatorFilter(caseCoordinatorID, adrCase.CaseNo))
// _adrMasterList.Add(adrCase);
if (CaseCoordinatorFilter(caseCoordinatorID, adrCase.CaseNo))
_adrMasterList.Add(adrCase);
}
}
}
}
}
if (otherActivitiesALL != null && adrOtherActivitiesALL != null)//other activities
{
foreach (var otherActivity in otherActivitiesALL)
{
foreach (var adrotherActivity in adrOtherActivitiesALL)
{
if (otherActivity.ActivityID == adrotherActivity.ActivityID)
{
var adrCase = FileMaintenanceBusiness.Instance.GetADRMasterInfobyKeyword(adrotherActivity.CaseNo, "CaseNo");
if (adrCase != null)
{
adrCase.ProcCode = otherActivity.ProcCode;
adrCase.ProcDateString = otherActivity.ProcDate.ToShortDateString();
//LGF 08012011 ADR-59: add case coordinator - add CaseCoordinatorFilter
//if (!_adrMasterList.Contains(adrCase) && CaseCoordinatorFilter(caseCoordinatorID, adrCase.CaseNo))
if(CaseCoordinatorFilter(caseCoordinatorID, adrCase.CaseNo))
_adrMasterList.Add(adrCase);
}
}
}
}
}
}
else if (_selectedProcCode == "DSP")
{
if (dispositions != null && adrDispos != null)
{
foreach (var dispo in dispositions)
{
foreach (var adrDispo in adrDispos)
{
if (dispo.DispositionID == adrDispo.DispositionID)
{
var adrCase = FileMaintenanceBusiness.Instance.GetADRMasterInfobyKeyword(adrDispo.CaseNo, "CaseNo");
//LGF 08012011 ADR-59: add case coordinator - add CaseCoordinatorFilter
if (adrCase != null && CaseCoordinatorFilter(caseCoordinatorID, adrCase.CaseNo))
{
adrCase.ProcCode = _selectedProcCode;
//adrCase.ProcDateString = dispo.ProcDate.ToShortDateString();
_adrMasterList.Add(adrCase);
}
}
}
}
}
}
else if (_selectedProcCode == "CAL")
{
if (calendarActivities != null && adrCalActivities != null)
{
foreach (var cal in calendarActivities)
{
foreach (var adrCal in adrCalActivities)
{
if (cal.CalendarItemID == adrCal.CalendarItemID)
{
var adrCase = FileMaintenanceBusiness.Instance.GetADRMasterInfobyKeyword(adrCal.CaseNo, "CaseNo");
//LGF 08012011 ADR-59: add case coordinator - add CaseCoordinatorFilter
if (adrCase != null && CaseCoordinatorFilter(caseCoordinatorID, adrCase.CaseNo))
{
adrCase.ProcCode = _selectedProcCode;
adrCase.ProcDateString = cal.ProcDate.ToShortDateString();
_adrMasterList.Add(adrCase);
}
}
}
}
}
}
else
{
if (otherActivities != null && adrOtherActivities != null)
{
foreach (var otherActivity in otherActivities)
{
foreach (var adrotherActivity in adrOtherActivities)
{
if (otherActivity.ActivityID == adrotherActivity.ActivityID)
{
var adrCase = FileMaintenanceBusiness.Instance.GetADRMasterInfobyKeyword(adrotherActivity.CaseNo, "CaseNo");
//LGF 08012011 ADR-59: add case coordinator - add CaseCoordinatorFilter
if (adrCase != null && CaseCoordinatorFilter(caseCoordinatorID, adrCase.CaseNo))
{
adrCase.ProcCode = _selectedProcCode;
adrCase.ProcDateString = otherActivity.ProcDate.ToShortDateString();
_adrMasterList.Add(adrCase);
}
}
}
}
}
}
GeneratePrintReport();
}
That is happening because of internal identity map pattern implementation. You can never have attached two entity instances with the same entity key to the same context. You must simply call Detach once you load the entity.
context.Detach(entity);
Or you can try to load entities as non tracked (I'm not sure if it solves this problem):
context.YourEntitySet.MergeOption = MergeOption.NoTracking;
// Now run the query from YourEntitySet
Anyway it is odd that you want two instances of the same entity with different values. You should use separate non entity object for your "report" and make projections to that object. You would not have to deal with this problem at all.