I have this relationship for my tables. Activity, Workstation, Platform, Part are the lookup tables.
I have ActivitWorkstation that contains (ActivityId, WorkstationId) foreign keys to Activity and Workstation tables.
I also have PlatformPart that contains (PlatformId, PartId) foreign keys to Platform and Part tables.
Lastly I have PartStaging table that has (ActivityWorkstationId, PlatformPartId) foreign keys to ActivityWorkstation and PlatformPart tables.
Below is my PartStaging Edit method:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(PartStagingVM partstagingvm)
{
if (ModelState.IsValid)
{
PartStaging partstaging = new PartStaging();
var activityWorkstation = db.ActivityWorkstations.FirstOrDefault(aw => aw.ActivityId == partstagingvm.ActivityId && aw.WorkstationId == partstagingvm.WorkstationId);
if (activityWorkstation == null)
{
activityWorkstation = new ActivityWorkstation
{
ActivityId = partstagingvm.ActivityId,
WorkstationId = partstagingvm.WorkstationId
};
db.Entry(activityWorkstation).State = EntityState.Added;
}
var platformPart = db.PlatformParts.FirstOrDefault(pp => pp.PlatformId == partstagingvm.PlatformId && pp.PartId == partstagingvm.PartId);
if (platformPart == null)
{
platformPart = new PlatformPart
{
PlatformId = partstagingvm.PlatformId,
PartId = partstagingvm.PartId
};
db.Entry(platformPart).State = EntityState.Added;
}
var partStaging = db.PartStagings.FirstOrDefault(ps => ps.ActivityWorkstationId == activityWorkstation.Id && ps.PlatformPartId == platformPart.Id);
if (partStaging != null && partStaging.Id != partstagingvm.Id)
{
TempData["Message"] = "The record already exists.";
}
else
{
partstaging.Id = partstagingvm.Id;
partstaging.ActivityWorkstationId = activityWorkstation.Id;
partstaging.PlatformPartId = platformPart.Id;
partstaging.Description = partstagingvm.Description;
db.Entry(partstaging).State = EntityState.Modified;
db.SaveChanges();
TempData["Message"] = "The record has been modified.";
}
return RedirectToAction("Edit");
}
return View(partstagingvm);
}
I can edit ActivityWorkstation. I can edit PlatformPart. It prevents me from updating when the record already exists. But I have a problem when editing the Description. It's supposed to be the simplest update since all I have to do is to assign:
partstaging.Description = partstagingvm.Description
But when I submit, I get this error:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
Source Error:
Line 216: partstaging.Description = partstagingvm.Description;
Line 217:
Line 218: db.Entry(partstaging).State = EntityState.Modified;
Line 219: db.SaveChanges();
Line 220:
What am I missing?
Related
Error happens on the var userinfo line... I'm new to ASP.NET MVC and I'm trying to create a sample project to learn ASP.NET MVC, but got stuck here. I searched other posts with similar errors, but the issue was with not saving .
public ActionResult Login(LoginViewModel c)
{
using (db = new DBEntities())
if (!ModelState.IsValid)
{
return View(c);
}
// error happens on this line of code
var userinfo = db.e_usr.Where(m => m.usr_ename == c.Email && m.usr_pswd == c.Password).FirstOrDefault();
if (userinfo != null)
{
Session["LoginID"] = userinfo.usr_ename;
Session["LoginUser"] = userinfo.usr_pswd;
return Redirect("Home/Index");
}
return null;
}
You're missing a code block (by specifying { .... }) after your using statement. Therefore, the db is valid for only the next statement - the check whether your ModelState is valid or not. After that, the db variable is gone.
Change your code to:
public ActionResult Login(LoginViewModel c)
{
using (db = new DBEntities())
{ // <<<==== add this leading curly brace!
if (!ModelState.IsValid)
{
return View(c);
}
// Simplify your LINQ here - define the condition directly
// in the ".FirstOrDefault()" method
var userinfo = db.e_usr.FirstOrDefault(m => m.usr_ename == c.Email && m.usr_pswd == c.Password);
if (userinfo != null)
{
Session["LoginID"] = userinfo.usr_ename;
Session["LoginUser"] = userinfo.usr_pswd;
return Redirect("Home/Index");
}
return null;
} /// <<<==== add these closing curly braces
}
According to the accepted answer here: The relationship could not be changed because one or more of the foreign-key properties is non-nullable
I've already deleted all child items before updating the entity using entity.Children.Clear():
public static JsonViewData AddOrUpdate(ModelDBContext context, GENCompanyViewModel companyModel, string userName, string userId)
{
try
{
var company = new GENCompany();
if (companyModel.Id > 0) //Update
{
company = context.Companies.Find(companyModel.Id);
context.Entry(company).State = EntityState.Modified;
company.Vehicles.Clear();
company.Phones.Clear();
company.Emails.Clear();
company.InjectFrom(companyModel);
company.DateUpdated = DateTime.Now;
}
else //Add
{
company.InjectFrom(companyModel);
company.CreatedById = new Guid(userId);
context.Companies.Add(company);
context.SaveChanges(userName);
}
if (companyModel.Vehicles.Any())
{
foreach (var vehicleModel in companyModel.Vehicles)
{
var vehicle = context.Vehicles.Find(vehicleModel.Id);
context.Entry(vehicle).State = EntityState.Unchanged;
company.Vehicles.Add(vehicle);
}
}
if (companyModel.Phones.Any())
{
foreach (var phoneModel in companyModel.Phones)
{
var phone = new GENCompanyPhone();
phone.InjectFrom(phoneModel);
company.Phones.Add(phone);
}
}
if (companyModel.Emails.Any())
{
foreach (var emailModel in companyModel.Emails)
{
var email = new GENCompanyEmail();
email.InjectFrom(emailModel);
company.Emails.Add(email);
}
}
context.SaveChanges(userName);
return new JsonViewData { IsSuccess = true, Message = "Added Successfully" };
}
catch (Exception ex)
{
return new JsonViewData { IsSuccess = false, Message = ex.InnerException?.InnerException?.Message ?? ex.InnerException?.Message ?? ex.Message };
}
}
and I get this error:
The operation failed: The relationship could not be changed because
one or more of the foreign-key properties is non-nullable. When a
change is made to a relationship, the related foreign-key property is
set to a null value. If the foreign-key does not support null values,
a new relationship must be defined, the foreign-key property must be
assigned another non-null value, or the unrelated object must be
deleted.
The problem occurs because you are trying to remove something from one end of a relationship but not the other and the thing on the other end is non nullable.
The easiest solution to this is to enable cascade deletes, then let EF worry about the details but if you have to handle this then what you need to do is remove all the items using as you are but also on each item set the related item to null and if this cannot be null you'll have to remove the related item too.
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 ;-)
Following code doesn't update my database for the record found and when looking at Azure SQL Profiler I see only this update statement.
update [dbo].[Identities]
set #p = 0
where ([Id] = #0)
and the code:
using (var db = new context())
{
var member = db.Identities.Include("Member").Include("Roles")
.FirstOrDefault(i => id.Value == i.nameidentifier && i.identityprovider == provider.Value);
if (member != null)
{
member.LastLoggedIn = DateTime.UtcNow;
db.SaveChanges();
}
}
Graph of objects stored in the database and the same object graph is serialized into a binary package. Package is transmitted over the network to the client, then it is necessary to merge data from the package and data from the database.
Source code of merge:
//objList - data from package
var objectIds = objList.Select(row => row.ObjectId).ToArray();
//result - data from Database
var result = SomeService.Instance.LoadObjects(objectIds);
foreach (var OSobj in objList)
{
var obj = result.Objects.ContainsKey(OSobj.ObjectId)
? result.Objects[OSobj.ObjectId]
: result.Objects.CreateNew(OSobj.ObjectId);
var targetObject = result.DataObjects.Where(x => x.ObjectId == OSobj.ObjectId).FirstOrDefault();
targetObject.StopTracking();
var importedProperties = ImportProperties(targetObject.Properties, OSobj.Properties);
targetObject.Properties.Clear();
foreach (var property in importedProperties)
{
targetObject.Properties.Add(property);
}
targetObject.StartTracking();
}
return result;
And code of ImportProperties method:
static List<Properties> ImportProperties(
IEnumerable<Properties> targetProperties,
IEnumerable<Properties> sourceProperties)
{
Func<Guid, bool> hasElement = targetProperties
.ToDictionary(e => e.PropertyId, e => e)
.ContainsKey;
var tempTargetProperties = new List<Properties>();
foreach (var sourceProperty in sourceProperties)
{
if (!hasElement(sourceProperty.PropertyId))
{
sourceProperty.AcceptChanges();
tempTargetProperties.Add(sourceProperty.MarkAsAdded());
}
else
{
sourceProperty.AcceptChanges();
tempTargetProperties.Add(sourceProperty.MarkAsModified());
}
}
return tempTargetProperties;
}
Server save incoming changes like this :
_context.ApplyChanges("OSEntities.Objects", entity);
_context.SaveChanges(SaveOptions.DetectChangesBeforeSave);
When the server tries to save the changes occur exception:
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.
But if I change the code of ImportProperties method, the error does not occur and the changes are saved successfully:
static List<Properties> ImportProperties(
IEnumerable<Properties> targetProperties,
IEnumerable<Properties> sourceProperties)
{
Func<Guid, bool> hasElement = targetProperties.ToDictionary(e => e.PropertyId, e => e).ContainsKey;
var tempTargetProperties = new List<Properties>();
foreach (var sourceProperty in sourceProperties)
{
if (!hasElement(sourceProperty.PropertyId))
{
var newProp = new Properties
{
ElementId = sourceProperty.ElementId,
Name = sourceProperty.Name,
ObjectId = sourceProperty.ObjectId,
PropertyId = sourceProperty.PropertyId,
Value = sourceProperty.Value
};
tempTargetProperties.Add(newProp);
}
else
{
var modifiedProp = new Properties
{
ElementId = sourceProperty.ElementId,
Name = sourceProperty.Name,
ObjectId = sourceProperty.ObjectId,
PropertyId = sourceProperty.PropertyId,
Value = sourceProperty.Value
};
modifiedProp.MarkAsModified();
tempTargetProperties.Add(modifiedProp);
}
}
return tempTargetProperties;
}
Why is there an exception?
When you transport an object graph (Entity with n-level deep navigation properties) to a client application the entities will record any changes made in their respective change trackers. When entity (or object graph) is sent back to the server side of the application basically all you need to do is:
try
{
using(Entities context = new Entities())
{
context.ApplyChanges(someEntity);
context.SaveChanges();
}
}
catch
{
...
}
I don't see the need of all the code above you posted. What are you trying to achieve with that code?