Could someone help me with this:
The code:
Role r = new Role { ID = 1, Name = "Members" };
ctx.Roles.Attach(r);
//Saving User
User u = new User {
Login = login,
Password = password,
Status = 1
};
u.Roles.Add(r);
ctx.Users.Add(u);
ctx.SaveChanges();
What I'm trying to do is to save a new user with an existing role. User and Role classes have a many-to-many relationship mapped by fluent-api as follows:
modelBuilder.Entity<User>()
.HasMany(u => u.Roles)
.WithMany(r => r.Users)
.Map(x => {
x.ToTable("USER_ROLE_XREF", dbsch);
x.MapLeftKey("ID_USER");
x.MapRightKey("ID_ROLE");
});
But when the SaveChanges is called I get this error:
{"The specified value is not an instance of type 'Edm.Decimal'\r\nParameter name: value"}
Actually, always I try to save related entities with a single SaveChanges() call I get the same error. so, what I had to do to figure this was to make multiple calls and so works well:
Role r = new Role { ID = 1, Name = "Members" };
ctx.Roles.Attach(r);
//Saving User
User u = new User {
Login = login,
Password = password,
Status = 1
};
ctx.Users.Add(u);
ctx.SaveChanges();
//Assigning Member Role
u.Roles.Add(r);
ctx.SaveChanges();
It is my understanding that EF support to save multiple changes with a single SaveChanges call, so I'm wondering what's wrong here.
The idea with the Attach() method is that you have an entity that is known to be in the DB but not being tracked by this context, right? My question to you is do you know for sure that this Role here:
Role r = new Role { ID = 1, Name = "Members" };
is something that exists already? If it doesn't, I don't think what you want to do is use
ctx.Roles.Attach(r);
rather it is that you'd write:
ctx.Roles.Add(r);
and then you could turn around and write
User u = new User {
Login = login,
Password = password,
Status = 1,
};
ctx.Users.Add(u);
u.Roles.Add(r);
ctx.SaveChanges();
The issue your first example has is that this new Role is really new to the DB so attaching it isn't what you'd want to do, rather you'd want to Add it.
And the single call to ctx.SaveChanges() should work just fine here.
Related
Please, help me to handle this situation:
I meaningly switched off AutoDetectChangesEnabled and I load my
entities AsNoTracked() meaningly either.
And I can't update many-to-many relationship in this case:
Here is the code of Update method:
public void Update(User user)
{
var userRoleIds = user.Roles.Select(x => x.Id);
var updated = _users.Find(user.Id);
if (updated == null)
{
throw new InvalidOperationException("Can't update user that doesn't exists in database");
}
updated.Name = user.Name;
updated.LastName = user.LastName;
updated.Login = user.Login;
updated.Password = user.Password;
updated.State = user.State;
var newRoles = _roles.Where(r => userRoleIds.Contains(r.Id)).ToList();
updated.Roles.Clear();
foreach (var newRole in newRoles)
{
updated.Roles.Add(newRole);
}
_context.Entry(updated).State = EntityState.Modified;
}
All simple fields, like Name, LastName updated. But the set
of Roles for User doesn't get updated - it stays the same.
I tried loading Roles using
_context.Entry(updated).Collection("Roles").Load();
But I can't update this loaded set in any way.
I searched for similar items but failed to find the answer, thought it definitely already exists.
I'm really sorry for possible dublicate.
PS. I want to add that I don't want to delete or update child entities at all.
A lot of existing answers suggest manually delete / add child entities to database in whole, but it is not suitable for me.
Roles are independent entities, any other user can use them.
I just want to update User_Role table in database, but I can't.
my problem is following - I have 2 tables (users, roles) with relation many-to-many. When I want to add new role for user, everything is OK, but I am not able to remove role from user.
List<Role> roles = new List<Role>();
roles.Add(role1); roles.Add(role3);
User user = db.users.Find(1);
user.roles = roles;
db.Entry(user).State = EntityState.Modified;
db.SaveChanges();
As I mentioned above, this code inserts new roles (1,3), but it does not delete role 2 (if exists).
I have no clue how to delete record from this junction table, can you help me please?
I know, that when I delete object from one table, all data related with this object are deleted from junction table, but i do not want to delete -> add object since it is related with another tables.
Here is another solution, you firstly have to load the related roles of the user before replacing the whole roles with your new list, like this:
User user = db.users.Include<User, ICollection<Role>>(u=>u.roles)
.FirstOrDefault(u=>u.ID == 1);
if(user != null){
user.roles = roles;
db.SaveChanges();
}
I supposed your roles navigation property has type of ICollection<Role> and the user's key property is ID, you can read more about using Include extension method (declared in QueryableExtensions.
You have to remove role2 from user.
List<Role> roles = new List<Role>();
roles.Add(role1); roles.Add(role3);
User user = db.users.Find(1);
user.roles.Remove(role2);
user.roles = roles;
db.SaveChanges();
First you need to include roles when loading your user:
var user = db.users
.Include(x=>x.Roles)
.Single(x=>x.Id == 1);
Then you need to identify Role to remove:
var roleToRemove = user.roles.Single(x => x.Id == 1);
And Finally you remove the role and save changes
user.Roles.Remove(roleToRemove);
db.SaveChanges();
For DbContext to be able to track changes you have to let it load collections first then just by modifying that collection DbContext will know how to update DB.
PS. You can use lazy load option if collection is large and you don't want to load all linked elements.
The problem is EF doesn't know you want to delete the older roles. When you set the Roles navigation property with the new list, you are only telling to EF that you want to add those new two roles, but you are not changing the state of the older roles to Deleted. There are two options to achieve what you need:
Changing the state to Deleted:
User user = db.users.Find(1);
// change the state to Deleted.
foreach(var r in user.Roles)
db.Entry(r).State = System.Data.Entity.EntityState.Deleted;
// Add the new roles
user.Roles.AddRange(roles);
Using the Remove method:
User user = db.users.Find(1);
// delete older roles
foreach(var r in user.Roles.ToList())
user.Roles.Remove(r);
// Add the new roles
user.Roles.AddRange(roles);
I'm creating a plugin for CRM 2013 (on-premise). My requirement is like this:
When a custom entity "Contract" creates, fire the plugin.
Contract has "1:1" relationship with the Quote entity.
Quote has a 1:N relationship with custom entity 'Property'.
For every properties that Quote has, create new Account records.
Link the newly created Account records to the Contract. The relationship Contract to Account is 1 to N.
I got all working however keep getting problems with #5. For some reason the plugin throws an error that Account ID does not exist.
Here's my code:
foreach ("**Property records found in Quote**")
{
var accountEntity = new Entity();
accountEntity = new Entity("account");
if (record.Attributes.Contains("name"))
{
accountEntity["name"] = record.Attributes["propertyname"];
}
else throw new InvalidPluginExecutionException(OperationStatus.Failed, "New Property Name is needed.");
service.Create(accountEntity);
var referenceCollection = new EntityReferenceCollection();
var relatedEntity = new EntityReference
{
Id = record.Id,
LogicalName = record.LogicalName
};
referenceCollection.Add(relatedEntity);
//The relationship schema name in CRM you are using to associate the entities.
var relRelationship = new Relationship
{
SchemaName = "new_new_contract_account"
};
service.Associate("account", ContractId, relRelationship, referenceCollection);
}
store the id of the newly created account:
var accountid = service.Create(accountEntity);
relatedEntity object must be set with the following properties:
var relatedEntity = new EntityReference
{
Id = accountid, /*the newly created account's id*/
LogicalName = "account"
};
replace your service.Associate line with:
service.Associate("new_contract", ContractId, relRelationship, referenceCollection);
I have an ASP.NET WebForms application with a (EF) database.
Basically the two tables I'm concerned with are Users and Roles.
In Roles there's: Id (pk), UserId (fk), Type : String - which contains either Admin, User, Moderator, Publisher, etc.
In Users there's: Id (pk), Email, NameFirst, NameLast, Password, Username.
In designer I connected Users with Roles so that in Roles -> UserId == Id of User.
And now, after creating a class that inherits from RoleProvider, in function GetRolesForUser(string username) I want to get the enum of all the roles of a user whose id is that of the username.
So for instance if I get a user Agon, I want to be able to get an enum of all his roles for later use and also return them as string[] in said method.
So for after hours of head-numbing attempts I've been getting consistent errors. Not sure where to go from here:
public override string[] GetRolesForUser(string username)
{
using (SMEntities db = new SMEntities())
{
User user = db.Users.First(x => x.Username == username);
}
//throw new NotImplementedException();
}
I'm not sure where enums come into play really on this one, but how about the following:
using (SMEntities db = new SMEntities())
{
User user = db.Users.First(x => x.Username == username);
return user.Roles.Select(r => r.Type).ToArray();
}
Some Background Information first:
I have a server that provides wcf soap services and a website that consumes those services. The server uses the entity framework to write/read to/from the database. During the process the entity object are transformed into DTOs and back when they return to the server.
I have the following entity model:
The DTOs are autogenerated.
Now to the problem:
The Participent is the 'start' object that is created with a VisibleStatus, a Status, a StatusMessage and two ContactGroups. After those are created I want to add Contact with the already created Participent and ContactGroup.
The problem here is, that the entity framework does not realize that the participent and the contact group already exist in the database and it creates a new database entry for those. Even if they already have an id. I think the problem lies in the fact that the entities are transformed to DTOs and back.
Asume the following client side code to generate the participent, visible status, status, status message and contact groups:
MT_Stammdaten_MeineKontakte_ParticipentDTO user = new MT_Stammdaten_MeineKontakte_ParticipentDTO();
user.MT_Participant_Id = endUserId;
user.StatusMessage = new StatusMessageDTO()
{
Text = "not defined",
Timestamp = DateTime.Now,
};
user.VisibleStatus = new VisibleStatusDTO()
{
Photo = null,
Status = new StatusDTO()
{
Value = "not defined"
},
Timestamp = DateTime.Now
};
user = cClient.AddParticipantMapping(user);
ContactGroupDTO defaultGroup = new ContactGroupDTO()
{
Name = CONTACTS_GROUP_STANDARD,
Description = CONTACTS_GROUP_STANDARD_DESC,
Participent = user,
};
ContactGroupDTO familyGroup = new ContactGroupDTO()
{
Name = CONTACTS_GROUP_FAMILY,
Description = CONTACTS_GROUP_FAMILY_DESC,
Participent = user,
};
defaultGroup = cClient.AddContactGroup(defaultGroup);
familyGroup = cClient.AddContactGroup(familyGroup);
And now the code for creating the contact:
MT_Stammdaten_MeineKontakte_ParticipentDTO participent = cClient.getUser(endUserId);
ContactGroupDTO group = cClient.GetContactGroup(1);
MT_Stammdaten_MeineKontakte_EndUser endUser = new MT_Stammdaten_MeineKontakte_EndUser()
{
MT_EndUser_Id = newContactId,
};
ContactDTO contact = new ContactDTO()
{
ContactGroup = group,
ContactGroupId = group.Id,
MT_Stammdaten_MeineKontakte_EndUser = endUser,
MT_Stammdaten_MeineKontakte_Participent = participent,
}
contact = cClient.AddContact(contact);
How can one tell the entity framework that the objects with an id already exist? Because of the fact, that most of the objects are nested, I'd prefer a rather generic solution.
You need to set the State on the existing objects to EntityState.Unchanged.
eg, for a group
context.ObjectStateManager
.ChangeObjectState
(group, System.Data.EntityState.Unchanged);