I am working on a project with ASP.Net MVC3 EF4.1 and relationships between tables through foreign keys. I am using the database first approach and have three tables: Calendar, Calendar Users and Users. The relationship is that a Calendar can have many users and users can have many calendars.
When someone is creating the calendar he/she is also supposed to select the number of users that will have access to the calendar. But now when I am about to save the changes to the database in the controller thats when I get confused. In the generated classes there are also virtual ICollections that I suppose represent the foreign keys somehow. But I can't figure out how I am supposed to handle them? So how is it supposed to work? Should I be able to add the changes to the virtual ICollections and then just do db.SaveChanges() and it will work by itself or am I supposed too handle that manually?
If I am supposed to handle it manually should I then add the users, add the calendar and then add the keys in the CalendarUsers table to bind them together? I've seen some examples from code first where they have clarified the relationship by entering code in the OnModelCreating method but when using Database first it just contains: throw new UnintentionalCodeFirstException();? Hoping you perhaps can clarify it for me a bit.
Added the classes generated by the DBcontext Generator below:
public partial class Calendar
{
public Calendar()
{
this.CalendarUsers = new HashSet<CalendarUser>();
}
public int CalendarId { get; set; }
public string CalendarTitle { get; set; }
public string CalendarDescription { get; set; }
public long UserId { get; set; }
public virtual User User { get; set; }
public virtual ICollection<CalendarUser> CalendarUsers { get; set; }
}
public partial class CalendarUser
{
public int CalendarUserId { get; set; }
public int CalendarId { get; set; }
public long UserId { get; set; }
public Nullable<bool> IsAdmin { get; set; }
public virtual Calendar Calendar { get; set; }
public virtual User User { get; set; }
}
public partial class User
{
public User()
{
this.Calendars = new HashSet<Calendar>();
this.CalendarUsers = new HashSet<CalendarUser>();
}
public long UserId { get; set; }
public string Name { get; set; }
public virtual ICollection<Calendar> Calendars { get; set; }
public virtual ICollection<CalendarUser> CalendarUsers { get; set; }
}
You can just create a new relationship between existing Calendar and User by setting the foreign key properties in a new instance of the CalendarUser:
var newCalendarUser = new CalendarUser
{
CalendarId = calendarId,
UserId = userId,
IsAdmin = true // or false
};
dbContext.CalendarUsers.Add(newCalendarUser);
dbContext.SaveChanges();
You can update the navigation properties and EF will take care of the foreign keys for you. For sample code that handles updates in a similar many-to-many relationship, see the Adding Course Assignments to the Instructor Edit Page section of this tutorial:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/updating-related-data-with-the-entity-framework-in-an-asp-net-mvc-application
Related
I have read the articles about Independent association from Ladislav. I have checked the information he provided in this link.
The information is quite helpful and it shed some light.
However I want to model my existing database which I was given access to. It has three tables Users, Certificates and Quiz .
At first I thought of modeling as independent association. Why? My Users table has primary key UserID and Certificates table has PK CertID and a column UserID which I can say is a foreign key. Just when I thought its a one to many relationship, I realized that some UserID in Users table are not found in Certificates table. However all UserID in Certificates can be found in Users table.
My question is should I use independent association and if so how to achieve this that my Users table become the principal class and the Certificates one the dependent. This is so that I can show or have values from my Users table then read values from my Certificate table in my asp.net mvc 3 application.
Please correct this code below which shows what I intend to achieve what I stated above:
public class Certificates
{
[Key]
public Users CertID { get; set; }
public int ID { get; set; }
public DateTime recvd { get; set; }
public int QuizID { get; set; }
}
public class Users
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string email { get; set; }
public ICollection<Certificates> Certificates { get; set; }
}
public class Quiz
{
public int QuizID { get; set; }
public string QuuizName { get; set; }
public int VolumeNo { get; set; }
public int mark { get; set; }
public ICollection<Certificates> Certificates { get; set; }
}
public class cpdContext : DbContext
{
public DbSet<Certificates> Certificates { get; set; }
public DbSet<Users> Users { get; set; }
public DbSet<Users> Quiz { get; set; }
Finally how do I show details view with information from these three classes with ability to add Users mark and tests. The relationship I want to model is 1 to many between Quiz and Certificates.
My Users table has primary key UserID and Certificates table has PK
CertID and a column UserID which I can say is a foreign key. Just when
I thought its a one to many relationship, I realized that some UserID
in Users table are not found in Certificates table. However all UserID
in Certificates can be found in Users table.
That's pretty normal for a one-to-many relationship where User is the principal and Certificate the dependent and you have a constraint enforced for the relationship.
I don't see this as an argument to decide for independent or foreign key associations. As far as I can tell you can map a database schema with both association types. The database schema shouldn't be the driving factor for the decision. Ladislav's posts you have linked explained it in all details. There are other points than the database schema that will guide the decision:
Architecture: Strict separation of object and relational world which might lead to the decision that you don't want a "foreign key" property as a relational artifact in your object model. This goes in favor of independent associations.
Ease of use: The additional foreign key property makes it easier to work with relationships, especially updating them, in some scenarios. This point is for foreign key associations.
Performance: EF is faster with foreign key associations in some situations with larger models.
Personally point 2 above tips the scales for me in most cases, but as said both is possible.
Mapping with foreign key associations (I omit all properties except PK, FK and navigation properties to keep it short):
public class Certificates
{
[Key]
public int CertID { get; set; }
[ForeignKey("User")]
public int UserID { get; set; }
[ForeignKey("Quiz")]
public int QuizID { get; set; }
public Users User { get; set; }
public Quiz Quiz { get; set; }
}
public class Users
{
public int ID { get; set; }
public ICollection<Certificates> Certificates { get; set; }
}
public class Quiz
{
public int QuizID { get; set; }
public ICollection<Certificates> Certificates { get; set; }
}
This assumes that both relationships are required, i.e. FKs in the database are not nullable. If they are you need to make the FK properties nullable as well (int?). Instead of data annotations you can use Fluent API, similar (but not exactly identical!) to the following example.
Mapping with independent associations:
public class Certificates
{
[Key]
public int CertID { get; set; }
public Users User { get; set; }
public Quiz Quiz { get; set; }
}
public class Users
{
public int ID { get; set; }
public ICollection<Certificates> Certificates { get; set; }
}
public class Quiz
{
public int QuizID { get; set; }
public ICollection<Certificates> Certificates { get; set; }
}
public class cpdContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entitiy<Users>()
.HasMany(u => u.Certificates)
.WithRequired(c => c.User) // or WithOptional
.Map(m => m.MapKey("UserID")); //<- DB FK column name
modelBuilder.Entitiy<Quiz>()
.HasMany(u => u.Certificates)
.WithRequired(c => c.Quiz) // or WithOptional
.Map(m => m.MapKey("QuizID")); //<- DB FK column name
}
}
The navigation properties in Certificates are not required (neither in the first nor the second example), you can remove them, if you don't need them, just use the parameterless WithRequired() then, and use Fluent API in the first example. (Possibly a [ForeignKey("UserID")] annotation on the Certificates collection in the Users class will work as well.)
I've been following the contoso university tutorial on Microsoft´s site and I have some doubts about how the Entity Framework is doing some stuff. Here we go...
On the beginning of the tutorial, we created three classes that will be turned into tables in my database.
I´d like to know when and how entity instantiates my classes to populate the objects.
Im using code-first approach.
Example:
The classes im using:
public class Course
{
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public decimal? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
public class Student
{
public int StudentID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
After following the tutorial I can now list all my Students and theirs respectives Course(s), as well as their properties in enrollments lists.
When did entity instantiated my Student classes and populated it with the respective Enrollment(s) lists?
How does it know what enrollments are linked to that student?
I dont see any new constructor() ever beeing called.
This might be simple but Im kinda lost here.
Thanks
It doesn't instantiate anything ahead of time, and it doesn't have to. And there won't be anything in the table representing the virtual property as such, either.
These virtual properties are used by EF at runtime to store navigation information, and the collections can be null or contain 0 or more elements. There's no magic, it's just by design that EF "new"s up the collection when it needs to.
If you want to be explicit about it, you can create a constructor on your POCO as well and set your collection equal to a new hashtable.
EF 4.1 in MVC3 and Lazy loading, using code first model
I am using Membership API for creating an account. Once the account is created successfully. I redirect to create a Contact record automatically.
contactId (auto database generated), userid (storing the user id that was generated by membership api)
The models are:
public class Contact
{
public int ContactID { set; get; }
public string UserId { set; get; }
public string LastName { set; get; }
public int? CompanyID { set; get; } // not sure if I need this as it will be NULL
public virtual Company CompanyInfo { set; get; }
}
next the user can click on create Company link or logout & login later to create the company record.
public class Company
{
public int CompanyID { set; get; }
public int ContactID { set; get; }
public string CompanyName { set; get; }
public virtual Contact Contacts { set; get; }
}
When the user decides to create company record, I am checking if company already exists, if exists I am just showing the contact information and the company information OR if not found I redirect to create company.
public ActionResult chckifCompanyFound()
{
int contactId = 1; //Assuming I retrieved the value
//I think I should get the data from Company table, if company data found then contact data could be retrieved using lazy loading?
Company c= db.Company.Include(c => c.Contacts).Where(x => x.ContactID == contactId).FirstOrDefault();
if(c == null)
//redirect to create company
else
// view data from c company object
}
currently it shows an exception once it tries to create contact record after membership API creates an account. I create the record like this:
Contact contact = new Contact();
contact.UserId = userId;
contact.LastName = lastName;
db.Contacts.Add(contact);
db.SaveChanges();
Exception:
Unable to determine the principal end of an association between the types 'D.Models.Contact' and 'D.Models.Company'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
thank you so much!
Try the following (from this link):
Contact cc = db.Contacts.Include( "CompanyInfo" ).Where(x => x.ContactID == product.ContactID).FirstOrDefault();
Try replacing your models with these:
public class Contact
{
public int ContactId { set; get; }
public string UserId { set; get; }
public int CompanyId { get; set; }
public string LastName { set; get; }
public virtual Company Company { set; get; }
}
public class Company
{
public int CompanyId { set; get; }
public string CompanyName { set; get; }
public virtual ICollection<Contact> Contacts { set; get; }
}
Your Contact needs a CompanyId since it has only a single company related to it, and it will act as a foreign key between that contact and the company. The navigation property CompanyInfo will be used for the lazy loading. Your Company object only needs the Contacts collection because the Contact is where the relationship is created in the database.
To answer your question about the query I need more information... where does the product come in to play? I don't see it referenced from the contact or company, but if you want to get the Company of a Contact, simply do this:
var company = dbContext.Contacts.Find(userId).Company;
Console.WriteLine("Company Name: {0}", company.CompanyName);
try:
Contact cc = db.Contacts.Include(c=>c.CompanyInfo).Where(x => x.ContactID == product.ContactID).FirstOrDefault();
Setup
Using MVC 3 + Code First
Here are my classes
public class Member
{
[Key]
public Guid ID { get; set; }
[Required]
public String Email { get; set; }
[Required]
public String FirstName { get; set; }
[Required]
public String LastName { get; set; }
public String Sex { get; set; }
public String Password { get; set; }
public String PasswordSalt { get; set; }
public DateTime RegisterDate { get; set; }
public DateTime LastOnline { get; set; }
public String SecurityQuestion { get; set; }
public String SecurityAnswer { get; set; }
public virtual ICollection<FamilyMember> Families { get; set; }
public virtual ICollection<Relationship> Relationships { get; set; }
}
public class Relationship
{
[Key]
public Guid ID { get; set; }
[ForeignKey("Member1")]
public Guid Member1ID { get; set; }
[ForeignKey("Member2")]
public Guid Member2ID { get; set; }
public Guid RelationshipTypeID { get; set; }
public virtual RelationshipType RelationshipType { get; set; }
public virtual Member Member1 { get; set; }
public virtual Member Member2 { get; set; }
}
Here is the problem
The database table "Relationship" is being created with the following columns:
ID, Member1ID, Member2ID, RelationshipTypeID, Member_ID
Why is it creating the Member_ID column?
I've seen this post in which the user has the same type of setup, but I am unsure of how to define the InverseProperty correctly. I tried using fluent API calls but from what I can tell they will not work here since I have two foreign keys referring to the same table.
Any help would be appreciated!
Member_ID is the foreign key column which EF created for the navigation property Member.Relationships. It belongs to a third association from Member.Relationships refering to an end endpoint which is not exposed in your Relationship entity. This relationship has nothing to do with the other two relationships from Relationship.Member1 and Relationship.Member2 which also both have an endpoint not exposed in Member.
I guess, this is not what you want. You need always pairs of endpoints in two entities to create an association. One endpoint is always a navigation property. The second endpoint can also be a navigation property but it is not required, you can omit the second navigation property.
Now, what is not possible, is to associate two navigation properties (Member1 and Member2) in one entity with one navigation property (Relationships) in the other entity. That is what you are trying to do apparently.
I assume that your Member.Relationships property is supposed to express that the member is either Member1 or Member2 in the relationship, or that it participates in the relationship, no matter if as Member1 or Member2.
Unfortunately you cannot express this in the model appropriately. You have to introduce something like RelationsshipsAsMember1 and RelationsshipsAsMember2 and for these two collection you can use the InverseProperty attribute as shown in the other question. In addition you can add a helper property which concats the two collections. But this is not a mapped property but readonly:
public class Member
{
// ...
[InverseProperty("Member1")]
public virtual ICollection<Relationship> RelationshipsAsMember1 { get; set; }
[InverseProperty("Member2")]
public virtual ICollection<Relationship> RelationshipsAsMember2 { get; set; }
public IEnumerable<Relationship> AllRelationships
{
get { return RelationshipsAsMember1.Concat(RelationshipsAsMember2); }
}
}
Accessing AllRelationships will cause two queries and roundtrips to the database (with lazy loading) to load both collections first before they get concatenated in memory.
With this mapping the Member_ID column will disappear and you will only get the two expected foreign key columns Member1ID, Member2ID because now you have only two associations and not three anymore.
You could also think about if you need the Relationships collection in the Member entity at all. As said, navigation properties on both sides are not required. If you rarely need to navigate from a member to its relationships you could fetch the relationships also with queries on the Relationship set, like so:
var relationships = context.Relationships
.Where(r => r.Member1ID == givenMemberID || r.Member2ID == givenMemberID)
.ToList();
...or...
var relationships = context.Relationships
.Where(r => r.Member1ID == givenMemberID)
.Concat(context.Relationships
.Where(r => r.Member2ID == givenMemberID)
.ToList();
This would give you all relationships the member with ID = givenMemberID participates in without the need of a navigation collection on the Member entity.
This is a question about Models, ASP.NET MVC 3 and relationships. I'm using the code-first approach.
Imagine this simple typical scenario of an User with its Blog Posts:
public class User
{
public virtual int UserId { get; set; }
public virtual string Nickname { get; set; }
public virtual List<Post> Posts { get; set; }
}
public class Post
{
public virtual int PostId { get; set; }
public virtual string BlogText { get; set; }
}
I made it very very simple.
Well. When I create a controller using the Controller with Read/Write actions and views using Entity Framework, the collection of Posts is not considered. It's being ignored. So, I can't get this relationship to work.
If, in place of the collection, there was a single object (public virtual string Email, for example) it works normally.
I'm asking myself and to you:
Why
How do I put references to collections in my model?
It should be a simple task, I really can't understand why it doesn't work.
Thank you.
POCO should look like below to have one-to-many relationship:
public class Post
{
public int Id { get; set; }
public string BlogText { get; set; }
public virtual User User { get; set; }
}
public class User
{
public int Id { get; set; }
public string Nickname { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}