EF 4.1 in MVC3 and Lazy loading, using code first model
Having difficulties designing correct models. Please take a look and let me know what am I doing wrong. How can I fix it.
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!
The answer is in your exception: The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
However, it looks like you have a 1 to 0..1 relationship here: Each company must have exactly 1 contact, but each contact can belong to zero or 1 company. Is this really your intent?
It seems to me that what you are really after is a 1 to * relationship, where each company can have MANY contacts, and each contact belongs to zero or 1 company.
Data annotations
public class Company // the principal
{
public int CompanyID { set; get; }
//public int ContactID { set; get; } Company is the principal
public string CompanyName { set; get; }
public virtual ICollection<Contact> Contacts { set; get; } // has many contacts
}
public class Contact
{
public int ContactID { set; get; }
public string UserId { set; get; }
public string LastName { set; get; }
// you do need CompanyId, but it will be nullable in the db
public int? CompanyID { set; get; }
[ForeignKey("CompanyID")]
public virtual Company CompanyInfo { set; get; }
}
Fluent API
Note: with fluent API, your entity does not need the [ForeignKey] attribute
modelBuilder.Entity<Company>()
.HasMany(principal => principal.Contacts)
.WithOptional(dependent => dependent.CompanyInfo)
.HasForeignKey(dependent => dependent.CompanyID);
You have a one-to-one relationship between Contact and Company and the exception says that EF cannot decide what's the principal (having the primary key) and what's the dependent (having the foreign key) because the navigation properties are "symmetric". EF demands that you specify this explicitly.
A few things to note about one-to-one relationships (they are more difficult to master with EF than one-to-many and even many-to-many relationships):
EF only supports Shared Primary Key Associations to define a one-to-one relationship. This means that the primary key values of the associated entities must be the same. And for one of the entities the primary key is a foreign key at the same time. You cannot use an independent foreign key.
Consequence of this point is that you can remove Contact.CompanyID and Company.ContactID. EF won't respect these properties as foreign key properties.
Second consequence is that one of the primary keys cannot be an autogenerated identity in the database because it must always have the same value as the other (the principal) entity.
You must decide which entity is the principal and which is the dependent. From your description I would guess that Contact is the principal (because you allow to have contacts without a company) and Company is the dependent (because there is no company without contact possible).
Then you can define a mapping with Fluent API in your derived context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// ...
modelBuilder.Entity<Contact>()
.HasOptional(ct => ct.CompanyInfo)
.WithRequired(cm => cm.Contacts);
// ...
}
I believe that this mapping will automatically ensure (if you create the DB with EF) that the primary key of Contact is an identity in the database, but the primary key of Company isn't. If not, you can turn this off explicitly:
modelBuilder.Entity<Company>()
.Property(c => c.CompanyID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
Related
Given the following:
public class Department
{
public int DepartmentID { get; set; }
public string Name { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public int DepartmentID { get; set; }
public virtual Department Department { get; set; }
}
If I turn lazy loading off and issue the following:
var departments = _DepartmentRepository.GetAll()
.Include(c => c.courses);
Then I get the answers with a Department object inside of them.
Is there a way I can just include the courses and not get back the Department object. For example can I just include one level (courses).
You are just including one level. The department object inside the course is there because EF has done some relationship fixup so that you can navigate to the department from the course.
If you don't want departments then just get the courses directly. That is context.Courses.ToList(); or via a courses repo if you have one.
When fetching entities EF will automatically populate navigation properties where it is already tracking the target object. This means if you do say:
// Load the department with a PK of 1
_DepartmentRepository.Find(1);
and then, using the same context, for example:
// Load a course with PK of 17
_CourseRepository.Find(17);
If this courses department id is 1, then EF will have automatically populated it's Department navigation property even though you didn't specify the include. You could stop this behavior by not making the Department navigation property virtual.
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.)
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.
I'm using the Entity Framework 4 code first approach to design my database in ASP MVC 3 and I ran into a bit of a hitch. I have a POCO class as below:
public class User
{
[Key]
public Guid UserID { get; set; }
public string UserName { get; set; }
public DateTime CreationDate { get; set; }
public string Email { get; set; }
public int Points { get; set; }
public Session ActiveSession { get; set; }
public ICollection<Session> InSessions { get; set; }
}
with Session as another one of my model classes defined elsewhere, with ICollection<User> as one of its properties. If I remove the public Session ActiveSession { get; set; } property from the User class, then the many-to-many mapping and UserSessions intermediate table are constructed correctly, but when I add the ActiveSession one-to-one mapping back in, it breaks the many-to-many mapping and the intermediate table is not constructed. Instead the Users table has a single foreign key to the Sessions table each for both the ActiveSession and InSessions properties. Any ideas why this is happening?
In your case EF thinks that ICollection<User> in Session class is the Many end of the one to many relationship created by ActiveSession.
So you need to configure mannualy
modelBuilder.Entity<User>().HasOptional(u => u.ActiveSession)
.WithMany()
.Map(u => u.MapKey("ForeignKeyColumn"));
modelBuilder.Entity<User>()
.HasMany(u => u.InSessions)
.WithMany(s => s.Users)
.Map(m =>
{
m.ToTable("UserSessions");
m.MapLeftKey("UserID");
m.MapRightKey("SessionID");
});