EF multiple relationships between entities - asp.net-mvc-3

I'm using the Code First approach to build the database in this problem. I have the following (partial) entity:
public class Tournament {
public int TournamentID { get; set; }
public String Name { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public int? SportID { get; set; }
public virtual Sport Sport { get; set; }
public virtual ICollection<Official> Officials { get; set; }
}
In the Official Entity I have this:
public class Official {
public int OfficialID { get; set; }
public String Surname { get; set; }
public String FirstName { get; set; }
public int? TournamentID { get; set; }
public virtual Tournament Tournament { get; set; }
public virtual ICollection<Match> Matches { get; set; }
}
Using some sample data and checking the SQL Server database, this works as I would expect it to. The tournament has a one-to-many relationship with officials.
The problem I'm having is that I would like the tournament to hold the primary key of an official as the head official. So I would add to the Tournament entity:
public int? OfficialID { get; set; } // foreign key to official table
public virtual Official HeadOfficial { get; set; } // navigation property
If I do this I get an attribute OfficialID and HeadOfficial_OfficialID in my Tournament table and I get TournamentID, Tournament_TournamentID and Tournament_TournamentID1 in my Officials table. I realise I now not only have a one-to-many relationship between Tournament and Official (seeing as a tournament can have many officials), but I also have a one-to-one relationship (seeing as a tournament can only have one head official).
How can I fix this problem?

You can fix the problem by giving EF a hint which navigation properties belong together. EF conventions cannot decide this anymore when you have two navigation properties in a class which refer to the same target class:
public class Tournament {
public int TournamentID { get; set; }
//...
public int? OfficialID { get; set; }
[ForeignKey("OfficialID")]
public virtual Official HeadOfficial { get; set; }
[InverseProperty("Tournament")] // the navigation property in Official class
public virtual ICollection<Official> Officials { get; set; }
}
It's also possible with Fluent API if you prefer that:
modelBuilder.Entity<Tournament>()
.HasOptional(t => t.HeadOfficial)
.WithMany()
.HasForeignKey(t => t.OfficialID);
modelBuilder.Entity<Tournament>()
.HasMany(t => t.Officials)
.WithOptional(o => o.Tournament)
.HasForeignKey(o => o.TournamentID);

Related

How to update an existing object in a many to many relationship (.Net 5)

I have been using the .Net 5 and EF Core 5 for a small web app. Given EF Core 5 supports many - many out of the box there is no need for a joining table.
I've run into an issue when updating a object that already exists in the DB. For my app I have Athletes and Parents which have the many - many relationship.
public class Athlete
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleName { get; set; }
public string Email { get; set; }
public string ContactNumber { get; set; }
public string Street { get; set; }
public int Postcode { get; set; }
public string City { get; set; }
public StateEnum State { get; set; }
public DateTime DateofBirth { get; set; }
public DateTime DateSignedUp {get; set;}
public virtual ICollection<Parent> Parents { get; set; }
}
public class Parent
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleName { get; set; }
public string Email { get; set; }
public string ContactNumber { get; set; }
public string Street { get; set; }
public int Postcode { get; set; }
public string City { get; set; }
public StateEnum State { get; set; }
public DateTime DateofBirth { get; set; }
public DateTime DateSignedUp {get; set;}
public virtual ICollection<Athlete> Athletes { get; set; }
}
When I try to update the existing athlete that has a relation ship with two other parents I get an error:
Violation of PRIMARY KEY constraint 'PK_AthleteParent'. Cannot insert
duplicate key in object 'dbo.AthleteParent'. The duplicate key value
is (31, 1)
[HttpPost]
public async Task<ActionResult<Athlete>> PostAthlete(Athlete athlete)
{
_context.Athletes.Update(athlete);
await _context.SaveChangesAsync();
return Ok(athlete));
}
From what I can tell when entity tries to update my Athlete it tries to insert new rows into the joining table even though the parents already exist in there. Is there a way to get entity to remove any records when the relationship is updated? Or is there a way to tell entity to take update the joining table to match the Athlete object that is passed in?
Given a simple example like this:
public class Foo {
Guid Id { get; set; }
ICollection<Bar> Bars { get; set; }
}
public class Bar {
Guid Id { get; set; }
ICollection<Foo> Foos { get; set; }
}
You can call clear() on a tracked instance of Foo, and then re-add the Bar instances that you want assigned. I've found this is a nice way to avoid the constraint exception - much easier than manually trying to figure out what Bars have changed.
var foo = context.Foos.Include(x => x.Bars).FirstOrDefault(x => x.Id == someGuid);
foo.Bars.Clear();
foo.Bars.Add(bar1);
foo.Bars.Add(bar2);
...
context.Update(foo);
context.SaveChanges();

ASP.NET Model Relationship

I'm currently learning ASP.NET MVC and Web API.
I'm trying to create a User Model. Users can have any number of UserContacts. UserContacts reference the User it is a contact of and the User who is the contact. I have made a model called UserContact because attached to this Model is additional information.
public class User
{
public int UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class UserContact
{
public int UserContactID { get; set; }
public int UserID { get; set; }
[ForeignKey("UserID"), Column(Order = 0)]
[Required]
public User User { get; set; }
public int ContactID { get; set; }
[ForeignKey("ContactID"), Column(Order = 1)]
[Required]
public User Contact { get; set; }
public DateTime ContactSince { get; set; }
}
So this gives me an error referring to cascading Delete. How do I set up a relationship like this where two foreign keys point to the same Model type? I have yet to grasp Entity Framework syntax as well. If I don't have an ICollection of UserContacts in the User model, does this hinder my ability to grab the UserContacts associated with that User?
When you have a foreign key and the foreign key columns are not nullable(means,required). EF will automatically tries to enable cascading delete on the relationsip. In your case, it will try to enable Cascading delete for both the foreign key columns and both of them points to the same user table! That is the reason you are getting this error. What if you have a UserContact record with Both UserId and ContactID points to the same User record. Cascading delete is confused now :)
Also, since one user can have more than one Contacts, We need a Contacts property on the User table to represent that. This will be a collection of UserContact's. Also this user can be a a contact of many other people. So Let's create another property for that.
public class User
{
public int UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<UserContact> Contacts { set; get; }
public ICollection<UserContact> ContactOf { set; get; }
}
public class UserContact
{
public int UserContactID { get; set; }
public int UserID { get; set; }
public User User { get; set; }
public int ContactID { get; set; }
public User Contact { get; set; }
public DateTime ContactSince { get; set; }
}
And in your DbContext class, We can configure the foreign key relation ships and tell EF to disable cascade delete using fluent configuration inside the overridden OnModelCreating method. The below code will disable cascading delete on both the the relationships. But for your error to go away. disabling on one foreign key is enough.
public class YourDbContext: DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<UserContact>()
.HasRequired<User>(g=>g.User)
.WithMany(g=>g.Contacts)
.HasForeignKey(g=>g.UserID)
.WillCascadeOnDelete(false);
modelBuilder.Entity<UserContact>()
.HasRequired<User>(g => g.Contact)
.WithMany(g => g.ContactOf)
.HasForeignKey(g => g.ContactID)
.WillCascadeOnDelete(false); // this one is not really needed to fix the error
base.OnModelCreating(modelBuilder);
}
public DbSet<User> Users { set; get; }
public DbSet<UserContact> UserContacts { set; get; }
}
This will create the tables like you wanted with the necessary foreign keys.
There is not enough information for EF to figure out the relationships on the other side, so yes, you need collections. You can use the InverseProperty annotation to clarify (or fluent api statements):
public class User
{
public int UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[InverseProperty("User")]
public Virtual ICollection<UserContact> Users{ get; set; }
[InverseProperty("Contact")]
public Virtual ICollection<UserContact> Contacts { get; set; }
}
public class UserContact
{
public int UserContactID { get; set; }
public int UserID { get; set; }
[ForeignKey("UserID"), Column(Order = 0)]
[Required]
public User User { get; set; }
public int ContactID { get; set; }
[ForeignKey("ContactID"), Column(Order = 1)]
[Required]
public User Contact { get; set; }
public DateTime ContactSince { get; set; }
}
http://www.entityframeworktutorial.net/code-first/inverseproperty-dataannotations-attribute-in-code-first.aspx

Entity Framework POCO Relationships

I am trying to implement code-first approach of entity framework. I have four entities UserInfo, Client, Admin and Account. I want relationships as:
Each Client has a UserInfo
Each Admin has a `UserInfo
Each Account is linked with a User(UserInfo)
Assuming these things i wrote the POCO models. With the relationships i want, is it correct ?Am i missing something?
public class UserInfo
{
public int UserInfoID { get; set; }
public Name Name { get; set; }
public Address Address { get; set; }
public Contact Contact { get; set; }
}
public class Admin
{
public int AdminID { get; set; }
public int UserInfoID { get; set; }
[ForeignKey("UserInfoID")]
public virtual UserInfo UserInfo { get; set; }
}
public class Client
{
public int ClientID { get; set; }
public CompanyDetails CompanyDetails { get; set; }
public int UserInfoID { get; set; }
[ForeignKey("UserInfoID")]
public virtual UserInfo UserInfo { get; set; }
}
public class Account
{
public int AccountID { get; set; }
[Required, Column("Balance"), Display(Name = "Account Balance")]
public double Balance { get; set; }
public int UserInfoID { get; set; }
[ForeignKey("UserInfoID")]
public virtual UserInfo UserInfo { get; set; }
}
What you have appears to be correct based on your requirements however I personally prefer the Entity Framework Model Builder when configuring your entities with Code First.
Using the model builder means that you don't have any attributes on your POCO entities which in turn means that you don't need an EF reference to use the entities.
Take a look at my article here for some more info on how to use the modelbuilder : http://blog.staticvoid.co.nz/2012/07/entity-framework-navigation-property.html

Entity doubts - following Contoso University Tutorial

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.

Entity Framework 4.1 - Segregating an entity into multiple tables (Code-First)

I'm trying to find out how I can re-use a simple 'Comment' entity type for multiple scenarios where something is 'commentable' in my application.
At the moment, I have a couple of entities that a user is able to post comments to. Examples include Blogs, Profiles and Photos - these can all be 'commented' on.
I'd like to be able to use the same 'Comment' class for each of these scenarios, but I don't want to end up with one HUGE table full of comments for everything. I figure it would be much more efficient to at least store a table of BlogComments, PhotoComments, and ProfileComments. At the moment, my Comment class looks like this:
public class Comment
{
[Key]
public int Id { get; set; }
public int ContextId { get; set; }
[StringLength(256)]
public string Content { get; set; }
public DateTime DatePosted { get; set; }
public virtual Member Author { get; set; }
}
Presumably, I'd need the 'ContextId' field to refer to the particular thing being commented on. This Id might be the Id of a Blog, a Profile or a Photo. I was hoping to be able to refer to comments much like a normal ICollection in these classes, and I have some code like this for the Photos as an example:
public class Photo
{
[Key]
public int Id { get; set; }
[StringLength(48)]
public string FileName { get; set; }
public virtual Member Owner { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
I've been pointed to various articles during my searches, but none really seem relevant to my particular situation. How can I map these comment collections to different tables, and avoid having a comment "super-table"?
Any help, pointers or advice would be hugely appreciated :)
You can create an abstract Comment class and inherit from it specific comments such as PhotoComment, ProfileComment. You will be able to map the comments to different tables.
public abstract class Comment
{
[Key]
public int Id { get; set; }
[StringLength(256)]
public string Content { get; set; }
public DateTime DatePosted { get; set; }
public virtual Member Author { get; set; }
}
public class PhotoComment : Comment
{
public int PhotoId { get; set; }
public virtual Photo Photo { get; set; }
}
public class Photo
{
[Key]
public int Id { get; set; }
[StringLength(48)]
public string FileName { get; set; }
public virtual Member Owner { get; set; }
public virtual ICollection<PhotoComment> Comments { get; set; }
}

Resources