I'm new to Identity Framework and maybe what I'm doing here is not the best approach, but either way, here is my scenario:
Beside the "Role" factor, some areas on my application should consider also if a given user is attached to a given "Company".
I created a new entity "Company" (very simple, only with Id and Name) and a relationship entity "UserCompany" (with the user and the company's Id). I tried to make it as similar as possible with the structure used between Roles and Users on Identity Framework.
In my ApplicationDbContext I added both DbSets and some logic for, for example, adding a list of companies to a user.
The problem that I'm facing is that "SaveChanges" does not apply the changes to the database.
*Edit: no error is thrown and the result of SaveChanges() is "1".
Here is a sample code from my ApplicationDbContext
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Company> Companies { get; set; }
public DbSet<UserCompany> UserCompanies { get; set; }
//constructors, etc...
public void AddToCompanies(string _userId, params string[] _companyIds)
{
foreach (var companyId in _companyIds)
{
UserCompanies.Add(new UserCompany()
{
UserId = _userId,
DataAreaId = companyId
});
}
int result = this.SaveChanges();
}
}
And here is how I mapped this "UserCompany" entity
public class UserCompany
{
[Key, Column(Order = 1), ForeignKey("ApplicationUser")]
public string UserId { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
[Key, Column(Order = 2), ForeignKey("Company")]
public string DataAreaId { get; set; }
public virtual Company Company { get; set; }
}
On my UserAdminController class I created a private ApplicationDbContext object that is responsible for calling this logic. I suspect there is some problem in the way I'm dealing with two diferent contexts to save this data (one inside the ApplicationUserManager object and this new one), but I'm not sure if this is really the problem or if I'm missing something else here.
Any help would be appreciated!
Is the UserCompany model supposed to be like this
public class UserCompany
{
[Key, Column(Order = 1)]
public string UserId { get; set; }
[ForeignKey("UserId ")]
public virtual ApplicationUser ApplicationUser { get; set; }
[Key, Column(Order = 2)]
public string DataAreaId { get; set; }
[ForeignKey("DataAreaId")]
public virtual Company Company { get; set; }
}
And check if you use TransactionScope and not commit it.
Hope it helps!
Related
We are using Entity Framework Code First for this project's database.
Our requirements call for a central 'Resource' table, with a single column of ResourceId (uniqueidentifier NOT NULL DEFAULT (newsequentialid())).
Various tables would use this table for their ID.
Profile - ProfileId (uniqueidentifier NOT NULL)
Organization - OrganizationId (uniqueidentifier NOT NULL)
Document = DocumentId (uniqueidentifier NOT NULL)
So, if I create a new Profile record, I would create a new Resource record, and use that sequentially created Guid as the ID for the new Profile record.
The reason for this is to prevent an Id from Profile ever being present as an Id for Organization. (I know that this is most likely improbable, but not impossible.)
Right now we define this with relationships like this:
public class Resource : BaseEntity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ResourceId { get; set; }
public virtual Profile Profile_ProfileId { get; set; }
//...
}
public class Profile : BaseEntity, IAuditableEntity
{
[Key]
public Guid ProfileId { get; set; }
public virtual Resource Resource { get; set; }
//...
}
public class ProfileMapping : EntityTypeConfiguration<Profile>
{
public ProfileMapping()
{
//Primary key
HasKey(t => t.ProfileId);
//Constraints
Property(t => t.ProfileId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
//...
ToTable("Profile");
//Create Relation
HasRequired(t => t.Resource).WithOptional(t => t.Profile_ProfileId);
}
}
Then, when we create a new Profile we do this (db being an instance of our DBContext):
var res = new Resource();
db.Resource.Add(res);
var newProfile = new Profile{
ProfileId = res.ResourceId,
IsActive = true
};
db.Profile.Add(newProfile);
However, I am wondering, could we define our classes/models to inherit from Resource and get better results?
Have any of you worked with a database structure like this?
Actually, since the GUIDs for ProfileId and OrganizationId are generated on the same database server, you have a 100% guarantee that they are unique. I am assuming that you will let the database server generate the GUIDs.
GUIDs might have a chance (a very small chance) to collide if they are generated on different machines.
Anyway, here is a direct answer to your question:
You can do something like this:
public class Resource
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ResourceId { get; set; }
}
public class Profile
{
[Key]
[ForeignKey("Resource")]
public Guid ProfileId { get; set; }
public Resource Resource { get; set; }
public string Name { get; set; }
public Profile()
{
Resource = new Resource();
}
}
Note how the Profile entity is creating a Resource entity in its constructor. Also note that the primary key for Profile is also a foreign key.
UPDATE:
Here is another solution that I think is better, and that will work also in the case where you want to access the Profile entity from the Resource entity:
I added a Profile property to the Resource entity:
public class Resource
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ResourceId { get; set; }
public virtual Profile Profile { get; set; }
}
Here is the profile entity:
public class Profile
{
[Key, ForeignKey("Resource"), DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid ProfileId { get; set; }
public Resource Resource { get; set; }
public string Name { get; set; }
}
Notice that I no longer create a Resource object in the constructor.
Instead, I create it whenever the entity is saved by overriding the SaveChanges method on the DbContext like this:
public class MyContext : DbContext
{
public DbSet<Resource> Resources { get; set; }
public DbSet<Profile> Profiles { get; set; }
public override int SaveChanges()
{
foreach (var profile in ChangeTracker.Entries<Profile>()
.Where(x => x.State == EntityState.Added))
{
profile.Entity.Resource = new Resource();
}
//Here you also need to do the same thing for other Entities that need a row in the Resources table (e.g. Organizations)
return base.SaveChanges();
}
}
I am trying to form a relationship of 2 tables to a 3rd, on a 1 to many basis. I have the following code:
public class CompanyInvolvement
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public DateTime StartDate { get; set; }
public DateTime? EndDate { get; set; }
public virtual Person Person { get; set; }
public virtual Company Company { get; set; }
}
public class Person
{
public Person()
{
CompanyInvolvements = new Collection<CompanyInvolvement>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[Required]
public string ClientIdReference { get; set; }
public virtual ICollection<CompanyInvolvement> CompanyInvolvements { get; set; }
}
public class Company
{
public Company()
{
Involvements = new Collection<CompanyInvolvement>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[Required]
public string ClientIdReference { get; set; }
[Required]
public string CompanyName { get; set; }
public virtual ICollection<CompanyInvolvement> Involvements { get; set; }
}
So effectively a Person can have many involvements in companies and a Company can have many people involved with it. The model builder is used like so:
modelBuilder.Entity<CompanyInvolvement>().HasRequired(x => x.Person).WithMany(x => x.CompanyInvolvements);
modelBuilder.Entity<CompanyInvolvement>().HasRequired(x => x.Company).WithMany(x => x.Involvements);
I originally created the relationship using the modelbuilder, specifying left and right keys (CompanyId and PersonId) and this worked great. But now I need the Start and End dates for an Involvement, I guess I needed to create a dedicated entity.
The question: When I use the above structure, I can create and read out involvements for a company and also see involvements for a Person. However, when I try to do the following:
var person = _context.People.FirstOrDefault(x => x.Id == personId);
var involvement = company.Involvements.FirstOrDefault(x => x.Person == person );
company.Involvements.Remove(involvement);
_context.SaveChanges();
I get the following error:
A relationship from the 'CompanyInvolvement_Company' AssociationSet is
in the 'Deleted' state. Given multiplicity constraints, a
corresponding 'CompanyInvolvement_Company_Source' must also in the
'Deleted' state.
I think my virtual properties in the 3 entities are correct, but I have the feeling the modelbuilder logic I have may be slightly misconfigured?
I finally figured out what I was doing wrong. I needed to remove the Id property from the CompanyInvolvement entity and add the following composite key:
[Key, Column(Order = 0)]
public Guid PersonId { get; set; }
[Key, Column(Order = 1)]
public Guid CompanyId { get; set; }
I'm guessing by convention, these two properties were then linked as foreign keys to the Person and Company entities respectively. I also removed the modelbuilder mapping as stated in my original question. Once these were done, deleting CompanyInvolvements worked as expected.
I'm using EF 5 rc on VS 2012 RC and got some issues. Pretty sure it's got to do with my knowledge in databases and EF than the version numbers of the software I use :)
So, I have 3 classes. User, Role and Right.
User class
public class User
{
[Key]
public int UserId { get; private set; }
[Required]
public string EmailAddress { get; internal set; }
[Required]
public string Username { get; internal set; }
public ICollection<Role> Roles { get; set; }
// More properties
}
Right Class
public class Right
{
public virtual int RightId { get; set; }
public virtual string Description { get; set; }
}
Role Class
public class Role
{
public virtual int RoleId { get; set; }
public virtual string Description { get; set; }
public virtual ICollection<Right> Rights { get; set; }
}
Context
class MyContext : DbContext
{
public virtual DbSet<User> Users { get; set; }
public virtual DbSet<Role> Roles { get; set; }
public virtual DbSet<Right> Rights { get; set; }
}
Now, I want to add roles to a user, and rights to a role. But I also want to make sure it's possible to add the same Right can be added to different roles.
var role1 = new Role()
{
Description = "role1"
};
var role2 = new Role()
{
Description = "role2"
};
var right = new Right()
{
Description = "right"
};
context.Rights.Add(right);
context.Roles.Add(role1);
context.Roles.Add(role2);
role1.Rights = new List<Right>();
role2.Rights = new List<Right>();
role1.Rights.Add(right);
role2.Rights.Add(right);
/**** ERROR ****/
context.SaveChanges();
I'm getting
InvalidOperationException: Multiplicity constraint violated. The role 'Role_Rights_Source' of the relationship 'Role_Rights' has multiplicity 1 or 0..1.
What am I doing wrong ?
Also, I don't feel right about creating a new list like
role1.Rights = new List<Right>();
role2.Rights = new List<Right>();
What's the recommended way to do this ? Rights property is null. So I can't add anything to it without newing it up.
The problem is the convention used by EF to infer the relation. It thinks that the relation is one-to-many but you want many-to-many (role can have multiple rights and the right can be used in multiple roles).
There are two options to solve this:
Option 1: Create navigation property in Right:
public class Right
{
public virtual int RightId { get; set; }
public virtual string Description { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
Now EF convention will detect the collection on both sides of the relation and correctly use many-to-many multiplicity instead of one-to-many
Option 2: Use Fluent-API to tell EF that you want many-to-many relation:
public class MyContext : DbContext
{
public virtual DbSet<User> Users { get; set; }
public virtual DbSet<Role> Roles { get; set; }
public virtual DbSet<Right> Rights { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Role>()
.HasMany(r => r.Rights)
.WithMany();
}
}
Now EF knows that the Right can be assigned to multiple roles even through the Right doesn't have navigation property to Role.
If the Role can be assigned to multiple users you will have to use many-to-many relation as well
Ladislav's Answer should work well for the error.
On your issue of being uncomfortable with using:
role1.Rights = new List<Right>();
You should just initialize the these properties to new Lists in the constructor, then it will be done for all instances of Role:
public class Role
{
public virtual int RoleId { get; set; }
public virtual string Description { get; set; }
public virtual ICollection<Right> Rights { get; set; }
public Role ()
{
this.Rights = new List<Right>();
}
}
I am implementing feature that allows users to follow each other.
I have database tables:
User{UserId, FirstName, LastName etc.}
Followings{FollowerUserId, FollowingUserId, CreatedOnDate etc.}
So I added EF class:
public class Follow
{
[Key, Column(Order = 1)]
public Guid FollowerUserId { get; set; }
[Key, Column(Order = 2)]
public Guid FollowUserId { get; set; }
public DateTime CreatedOnDate { get; set; }
public virtual User Follower { get; set; }
public virtual User Following { get; set; }
}
The last two virtual properties couse issue.
When I call:
var model = con.Follows.Where(x => x.FollowerUserId == uid);
I get following exception:
Invalid column name 'Following_UserId'.
The issue is probably caused because of two User objects in one class. Any idea how to workaround this?
UPDATE
public class User
{
public Guid UserId { get; set; }
...
public virtual ICollection<Follow> Following { get; set; }
public virtual ICollection<Follow> Followers { get; set; }
}
I think the reason is that the foreign key properties (FollowerUserId and FollowUserId) and navigation properties (Follower and Following) do not respect the naming conventions so that EF is unable to recognize the first properties as foreign keys. You can fix the problem by specifying the FK properties explicitly using the [ForeignKey] attribute:
public class Follow
{
[Key, Column(Order = 1), ForeignKey("Follower")]
public Guid FollowerUserId { get; set; }
[Key, Column(Order = 2), ForeignKey("Following")]
public Guid FollowUserId { get; set; }
public DateTime CreatedOnDate { get; set; }
public virtual User Follower { get; set; }
public virtual User Following { get; set; }
}
Edit
A least the second property doesn't respect the naming convention, the first one looks OK. So, alternatively you can fix the problem by renaming the second FK property FollowUserId into:
public Guid FollowingUserId { get; set; }
...because the navigation property is called Following.
Edit 2
About your UPDATE: You need to add the [InverseProperty] attribute to tell EF which navigation properties belong together:
public class Follow
{
[Key, Column(Order = 1), ForeignKey("Follower")]
public Guid FollowerUserId { get; set; }
[Key, Column(Order = 2), ForeignKey("Following")]
public Guid FollowUserId { get; set; }
public DateTime CreatedOnDate { get; set; }
[InverseProperty("Followers")] // refers to Followers in class User
public virtual User Follower { get; set; }
[InverseProperty("Following")] // refers to Following in class User
public virtual User Following { get; set; }
}
I'm new to asp.net, mvc3 and entity framework.
I'm trying to develop a mvc3 programm with entity framework and code-first.
So I have two classes with a many-to-many relationship.
One class called "User" the other one is "Course".
public class Course : IValidatableObject
{
[...]
public virtual ICollection<User> Users { get; set; }
[...]
}
public class User : IValidatableObject
{
[...]
public virtual ICollection<Course> Courses { get; set; }
[...]
}
So, this works. But now I need an additional field which safes the status of the registration for a course.
Is there an easy way I don't know?
I tried it this way:
public class Course : IValidatableObject
{
[...]
public virtual ICollection<CourseUser> CourseUsers { get; set; }
[...]
}
public class User : IValidatableObject
{
[...]
public virtual ICollection<CourseUser> CourseUsers { get; set; }
[...]
}
public class CourseUser
{
[Key, ForeignKey("Course"), Column(Order = 0)]
public int Course_ID { get; set; }
[Key, ForeignKey("User"), Column(Order = 1)]
public string User_ID { get; set; }
public int Status { get; set; } //{ pending, approved }
public virtual Course Course { get; set; }
public virtual User User { get; set; }
}
But this makes it much more difficult to add or edit related data.
For example I didn't managed it yet to automatically add the user who created the course to the CourseUsers table.
No there is no easier way to do that. Once you add any additional field to your junction table it must be mapped as entity to allow you access to that field. It is not pure many-to-many relation any more. It is a new entity in your model with two one-to-many relations.