Foreign keys updating on creation but not on edit - c#

In my application, users are able to be assigned roles. In the setup page for this, I have the following code:
foreach (string userRole in roleArray)
{
OrganizationRole orgRole = signedUpOrg.Roles.FirstOrDefault(target => target.Name == userRole && target.OrganizationId == signedUpOrg.OrganizationId);
if (orgRole != null)
{
OrganizationUser_OrganizationRole existingUserRole = orgRole.OrganizationUser_OrganizationRole.FirstOrDefault(target => target.Organization_User.User.UserId == orgUser.User.UserId &&
target.OrganizationRole.Name == userRole &&
target.OrganizationRole.OrganizationId == signedUpOrg.OrganizationId);
if (existingUserRole == null || orgUser.User.UserId == 0) // new user role to new users or existing users with new roles
{
orgRole.OrganizationUser_OrganizationRole.Add(new OrganizationUser_OrganizationRole
{
Organization_User = orgUser,
OrganizationRole = orgRole
});
}
}
}
With this, we are able to cycle through the roles which are to be assigned and save them in the database. This works perfectly fine on creating the user and their roles, but on editing there is no change. The code seems to be hit at all the crucial points with the correct data (roles, etc) but there is no reflection in the database or on the front end.
This code is in a method called SaveUsers in a class called CommonUtilities and is called in an AdministrationController with the following code:
CommonUtilities.SaveUsers(viewModel);
Can anyone possibly think of any reasons as to why this would work correctly on creation but not while editing? Many thanks in advance and I will be more than willing ot clarifiy on any points.

A similar issue drove me to choose NHibernate over EF as it can update children and grandchildren collections (probably even deeper levels but I have not tried that). Adjusting ObjectState like the answers referred to by #JhonatasKleinkauff seemed the only answer in EF but was not satisfactory in our case. This seems to only happen when you disconnect from the ObjectContext between retrieval and saving the parent object.

Related

What is the best way to iterate through a C# object so only the modified fields are updated?

Please excuse my limited knowledge of Entity Framework/C#. I'm sure there is an easy way to do what I wish to do.
I want to iterate through an object that I received from the database in order to check which fields are different from my update DTO. I wish to do this so only the changed fields are updated in the objFromDb. I am aware that if I only change the fields in the objFromDb that are modified, EF will only update the changed fields. I'm not sure if this is necessary or if there is a better way to do it.
Here is my update code (works but not the way I want it to) with the individual properties as well as the commented code that I was trying to accomplish. I don't like hard-coding the individual properties as this will require maintenance in the event that the object is changed.
public T_IFS_EmployeeDTO Update(T_IFS_EmployeeDTO ojbDTO)
{
var objFromDb = _db.T_IFS_Employee.FirstOrDefault(u => u.EmployeeID == ojbDTO.EmployeeID);
if (objFromDb != null)
{
//foreach (var prop in objFromDb.GetType().GetProperties(BindingFlags.Instance))
//{
// if (prop.GetValue(objFromDb) != prop.GetValue(ojbDTO))
// {
// objFromDb.prop.GetValue(objFromDb) = prop.GetValue(ojbDTO);
// }
//}
if (objFromDb.FirstName != ojbDTO.FirstName) objFromDb.FirstName = ojbDTO.FirstName;
if (objFromDb.LastName != ojbDTO.LastName) objFromDb.LastName = ojbDTO.LastName;
if (objFromDb.UserName != ojbDTO.UserName) objFromDb.UserName = ojbDTO.UserName;
if (objFromDb.Password != ojbDTO.Password) objFromDb.Password = ojbDTO.Password;
if (objFromDb.AccessLevel != ojbDTO.AccessLevel) objFromDb.AccessLevel = ojbDTO.AccessLevel;
_db.T_IFS_Employee.Update(objFromDb);
_db.SaveChanges();
return _mapper.Map<T_IFS_Employee, T_IFS_EmployeeDTO>(objFromDb);
}
return ojbDTO;
I'm sure there is an easy way to do this but I haven't been able to figure it out. I do appreciate your time.
-Edit-
I think the following will work but will EF know if a field has not been modified and is it possible that it is as simple as this:
var objFromDb = _db.T_IFS_Employee.FirstOrDefault(u => u.EmployeeID == ojbDTO.EmployeeID);
var objFromCall = _mapper.Map<T_IFS_EmployeeDTO, T_IFS_Employee>(ojbDTO);
if (objFromDb != null)
{
objFromDb = objFromCall;
Entity Framework Core will check the values of your entity and check them against a snapshot of what they were like when you loaded them. See this for the details.
So you should be able to do:
var objFromDb = _db.T_IFS_Employee.FirstOrDefault(u => u.EmployeeID == ojbDTO.EmployeeID);
if (objFromDb != null)
{
_mapper.Map<T_IFS_EmployeeDTO, T_IFS_Employee>(ojbDTO, objFromDb);
//This overload of .Map sets properties in an existing object, as
//opposed to creating a new one
}
This will overwrite all properties in objFromDb with values from objDTO, But only the ones that are different will be written to the database when you call SaveChanges().
Setting objFromDb to objFromCall will overwrite your reference from the database and this won't be tracked at all.
And there's no need to call .Update() if you received the object from the DbContext and you haven't disabled change tracking.

EF: Searching for the newly created object in Database and update the old one in conflict

We have a model named Model1 and it has two attributes. Model1.Time, Model1.Text and Model1.Value, of course it has also ID as the requirement.
We are receiving the values from a SOAP service and registering them to our database but the question is: We have to check if these values are available already or not. What is the best possible way to do it?
Basically we are using a linq query.
DataContext db = new DataContext();
var x = (from y in db.Models where y.Value != new.Value && y.Text != new.Text select y).toList();
if { (x.Count == null) db.Models.Add(new); }
else {
foreach (var y in x) {
if (y.Time < new.Time) {
y.Time = new.Time;
y.Value = new.Value;
y.Text = new.Text;
db.ObjectStateManager.ChangeObjectState(y, EntityState.Modified);
}
}
db.SaveChanges();
}
Do you think that; this is the only possible approach or is there any more proper solution?
If you don't have the ID available from the service then I think what you have is the best you can do. or at least the approximate best. Hard to give you a better solution without knowing more about your use case or what you are trying to save.
Does what you have work? Is there something more specific about the code you posted you need help with?

Entity Framework creating new entity with relationship to existing entity, results in attempt to create new copy of the existing entity

I am trying to create a new user object with a specific Role. The "Role" is an existing entity in EF. I have googled, and stackoverflowed until I am blue in the face, and I have tried all the stuff that seems to be working for everyone else. But when I try to save my new user object, it first tries to create a new "Role", instead of just creating the new user object with a reference to the existing Role.
What am I doing wrong?
Role myRole = new Role { ID = myUser.Role.ID };
myObjectContext.Roles.Attach(myRole);
myUser.Role = myRole;
if (myUser.ID == 0)
{
myObjectContext.Users.AddObject(myUser);
}
else
{
if (myUser.EntityState == System.Data.EntityState.Detached)
{
myObjectContext.Users.Attach(myUser);
}
myObjectContext.ObjectStateManager.ChangeObjectState(myUser, System.Data.EntityState.Modified);
}
myObjectContext.SaveChanges(SaveOptions.None);
EDIT - AFTER MORE TESTING...
Ok.. so I have discovered some portion of the "cause" anyway. I still don't know why it does this and need help.
Basically, there are two sets of data I am attaching to my new User object. One is the "Role" which is a FK to a Role table that contains the Role. This shows up as a navigation property on the User like "User.Role".
The second set of data is a collection of objects called "FIPS", which are a many-to-many relationship between the User and another table called FIPS. There is a relationship table between them, that simply contains two columns, each a foreign key to User and FIPS, respectively. The FIPS for a user are also a navigation property that is referenced like "User.FIPS".
Here is the whole code showing the assignment of the FIPS and Role to the User object prior to saving the context.
List<string> fipsList = new List<string>();
foreach (FIPS fips in myUser.FIPS)
{
fipsList.Add(fips.FIPS_Code);
}
myUser.FIPS.Clear();
foreach (string fipsCode in fipsList)
{
FIPS myFIPS = new FIPS { FIPS_Code = fipsCode };
myObjectContext.FIPSCodes.Attach(myFIPS);
myUser.FIPS.Add(myFIPS);
}
Role myRole = new Role { ID = myUser.Role.ID };
myObjectContext.Roles.Attach(myRole);
myUser.Role = myRole;
if (myUser.ID == 0)
{
myObjectContext.Users.AddObject(myUser);
}
else
{
if (myUser.EntityState == System.Data.EntityState.Detached)
{
myObjectContext.Users.Attach(myUser);
}
myObjectContext.ObjectStateManager.ChangeObjectState(myUser, System.Data.EntityState.Modified);
}
myObjectContext.SaveChanges(SaveOptions.None);
I set up my watch to check the status of "myObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added)" to see when things were being added to this.
As soon as the first Related object is added to the User object, the second Related object that hasn't yet been attached to the context, is added to the context with an EntityState of "Added".
.... Gonna see if there is a way to avoid attaching the related entities to the User entity until after they have all been attached to the context.
--FOLLOWUP--
Ok.. well I changed the order of the code so that the related entities were attached to the context before being assigned to the User entity.. but as soon as the first related entity is assigned, the second related entity is shown as "added" in the ObjectStateEntries.
So, then I changed it to the following order:
Attach all related entities to context.
Remove existing relationships on the user object to related entity
types.
Assign related entities to user entity.
Save user entity.
And.. now.. it works.. omg it works... ! =)
It's been a while since I wrote the code below, but I vaguely recall running into the same problem and it was occurring because the role being added was currently being tracked by the context, so attaching the stub has the effect of adding a new role with the same Id.
In the following code, I check the ChangeTracker first and use an existing entry if the role is being tracked.
// add roles that are in dto.Roles, but not in resource.Roles
// use the change tracker entry, or add a stub role
var rolesToAdd = fromDto.Roles.Where(r => !toResource.Roles.Any(role => role.Id == r)).ToList();
var roleEntries = dbContext.ChangeTracker.Entries<Role>();
foreach (var id in rolesToAdd)
{
var role = roleEntries.Where(e => e.Entity.Id == id).Select(e => e.Entity).FirstOrDefault();
if (role == null)
{
role = new Role { Id = id };
dbContext.Set<Role>().Attach(role);
}
toResource.Roles.Add(role);
}
Why are you creating a new instance of your Role entity if it already exists in the database?
Anyway, if you want to manually attach your new instance to the context, it should work if the ID of the attached instance exists in the database. But in your case the following lines are a bit strange:
Role myRole = new Role { ID = myUser.Role.ID };
myObjectContext.Roles.Attach(myRole);
myUser.Role = myRole;
You first create a new Role that has an ID that comes from an existing Role instance (myUser.Role) then you attach your new instance then finally you affect again your instance to the user it comes from.
There's definitely something wrong here.
If your Role already exists (and it appears to be the case here as you wrote myUser.Role.ID on the first line, so I assume), why are you creating a new instance.
Drop those 3 lines.
Get your Role from the database. Then affect the Role that comes from the database to the myUser.Role property.
This is how I did it in my case.
Its a similar case where Item contains ICollection<Attribute> .Here no update is done , adding already existing attribute to the item is needed.
First I looped through each attribute inside the item.
I had to first detach it from the local
context.Set<Model.Attribute>().Local
.Where(x => x.Id == attr.Id)
.ToList().ForEach(p => context.Entry(p).State = EntityState.Detached);
Then I attached .
context.Set<Model.Attribute>().Attach(attr);
Then I reloaded the datas to it .
context.Entry(attr).Reload();
Try using this instead of the first three lines (which shouldn't be necessary at all, if the user object already knows it's role's ID and is discarded anyway):
int id = myUser.Role.ID; // Role should be NULL, if the user is actually new...
// could it be that you wanted to write myUser.RoleID?
Role myRole = myObjectContext.Roles.FirstOrDefault(x => x.ID == id);
myUser.Role = myRole;

Entity Framework 4 - Username and Email columns unique problem

I have a User entity in my entity model:
Username and Email should be unique but for now EF4 doesn't support this.
So I wrote this code to ensure uniqueness :
public void CreateNewUser(string i_UserName, string i_Email)
{
using (ModelContainer context = new ModelContainer())
{
User usr;
usr = context.UserSet.Where(u => u.Username == i_UserName).SingleOrDefault();
if (usr != null)
throw new Exception("Username not unique");
usr = context.UserSet.Where(u => u.Email == i_Email).SingleOrDefault();
if (usr != null)
throw new Exception("Email not unique");
context.UserSet.AddObject(new User() { Username = i_UserName, Email = i_Email });
context.SaveChanges();
}
}
If this is right approach, do I have way to automatically preform this code whenever context.UserSet.AddObject() is called? Or a more elegant way does exist?
I don't know of a more elegant way. Rather than using SingleOrDefault, I think you want something like:
bool isUniqueUserName = !context.UserSet.Any(u => u.Username == i_UserName);
for performance. Any() stops at the first match and doesn't have to enumerate the entire sequence.
Correct way is defining Unique index in database and catching an exception. These checks in code must be much more complex then simple: say me if the user already exists. First of all this is not only problem of insert but user can also usually modify his email. Another problem is concurrency - in very corner case you can have two users inserting same records in the same time. In both threads test queries can return that user name and email are not registered but during the saving one thread will get an exception (if you have unique index in db) or duplicit record will be created. If you want to avoid it you must lock your database records and lock table for insertion (serializable transaction). Such operation can decrease throughput of your application.
Another fact is that you will do 2 additional queries before each insert and at least one query before each update.
I use the same approach. Just ensure that you have one method that insert it and you should be find. Similar to factory method pattern.
Branching off of #ysrb's answer (having a single method that does the User insert), a more foolproof solution might be to hook into the ObjectContext.SavingChanges event and do your test there if a User entity is being saved. This way you can be sure your logic always fires.
Example:
IEnumerable<ObjectStateEntry> changes = this.ObjectStateManager.GetObjectStateEntries(EntityState.Added);
foreach (ObjectStateEntry stateEntryEntity in changes)
{
if (!stateEntryEntity.IsRelationship && stateEntryEntity.Entity != null)
{
if (stateEntryEntity.Entity is User)
{
//Do you work here
}
}
}

C# - Entity Framework - Understanding some basics

Model #1 - This model sits in a database on our Dev Server.
Model #1 http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/bdb2b000-6e60-4af0-a7a1-2bb6b05d8bc1/Model1.png
Model #2 - This model sits in a database on our Prod Server and is updated each day by automatic feeds. alt text http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/4260259f-bce6-43d5-9d2a-017bd9a980d4/Model2.png
I have written what should be some simple code to sync my feed (Model #2) into my working DB (Model #1). Please note this is prototype code and the models may not be as pretty as they should. Also, the entry into Model #1 for the feed link data (mainly ClientID) is a manual process at this point which is why I am writing this simple sync method.
private void SyncFeeds()
{
var sourceList = from a in _dbFeed.Auto where a.Active == true select a;
foreach (RivWorks.Model.NegotiationAutos.Auto source in sourceList)
{
var targetList = from a in _dbRiv.Product where a.alternateProductID == source.AutoID select a;
if (targetList.Count() > 0)
{
// UPDATE...
try
{
var product = targetList.First();
product.alternateProductID = source.AutoID;
product.isFromFeed = true;
product.isDeleted = false;
product.SKU = source.StockNumber;
_dbRiv.SaveChanges();
}
catch (Exception ex)
{
string m = ex.Message;
}
}
else
{
// INSERT...
try
{
long clientID = source.Client.ClientID;
var companyDetail = (from a in _dbRiv.AutoNegotiationDetails where a.ClientID == clientID select a).First();
var company = companyDetail.Company;
switch (companyDetail.FeedSourceTable.ToUpper())
{
case "AUTO":
var product = new RivWorks.Model.Negotiation.Product();
product.alternateProductID = source.AutoID;
product.isFromFeed = true;
product.isDeleted = false;
product.SKU = source.StockNumber;
company.Product.Add(product);
break;
}
_dbRiv.SaveChanges();
}
catch (Exception ex)
{
string m = ex.Message;
}
}
}
}
Now for the questions:
In Model #2, the class structure for Auto is missing ClientID (see red circled area). Now, everything I have learned, EF creates a child class of Client and I should be able to find the ClientID in the child class. Yet, when I run my code, source.Client is a NULL object. Am I expecting something that EF does not do? Is there a way to populate the child class correctly?
Why does EF hide the child entity ID (ClientID in this case) in the parent table? Is there any way to expose it?
What else sticks out like the proverbial sore thumb?
TIA
1) The reason you are seeing a null for source.Client is because related objects are not loaded until you request them, or they are otherwise loaded into the object context. The following will load them explicitly:
if (!source.ClientReference.IsLoaded)
{
source.ClientReference.Load();
}
However, this is sub-optimal when you have a list of more than one record, as it sends one database query per Load() call. A better alternative is to the Include() method in your initial query, to instruct the ORM to load the related entities you are interested in, so:
var sourceList = from a in _dbFeed.Auto .Include("Client") where a.Active == true select a;
An alternative third method is to use something call relationship fix-up, where if, in your example for instance, the related clients had been queried previously, they would still be in your object context. For example:
var clients = (from a in _dbFeed.Client select a).ToList();
The EF will then 'fix-up' the relationships so source.Client would not be null. Obviously this is only something you would do if you required a list of all clients for synching, so is not relevant for your specific example.
Always remember that objects are never loaded into the EF unless you request them!
2) The first version of the EF deliberately does not map foreign key fields to observable fields or properties. This is a good rundown on the matter. In EF4.0, I understand foreign keys will be exposed due to popular demand.
3) One issue you may run into is the number of database queries requesting Products or AutoNegotiationContacts may generate. As an alternative, consider loading them in bulk or with a join on your initial query.
It's also seen as good practice to use an object context for one 'operation', then dispose of it, rather than persisting them across requests. There is very little overhead in initialising one, so one object context per SychFeeds() is more appropriate. ObjectContext implements IDisposable, so you can instantiate it in a using block and wrap the method's contents in that, to ensure everything is cleaned up correctly once your changes are submitted.

Categories

Resources