Given the following Domain Entities:
public class Person {
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual ISet<PersonClub> Clubs { get; set; }
public Person() {
this.Clubs = new HashSet<PersonClub>();
}
}
public class Club {
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual ISet<PersonClub> Members { get; set; }
public Club() {
this.Persons = new HashSet<PersonClub>();
}
}
public class PersonClub {
public virtual Guid Id { get; set; }
public virtual Person Person { get; set; }
public virtual Club Club { get; set; }
}
and DTO's:
public class PersonDTO {
public Guid Id { get; set; }
public string Name { get; set; }
public ISet<ClubDTO> Clubs { get; set; }
public PersonDTO() {
this.Clubs = new HashSet<ClubDTO>();
}
}
public class ClubDTO {
public Guid Id { get; set; }
public string Name { get; set; }
public ISet<PersonDTO> Members { get; set; }
public ClubDTO() {
this.Members = new HashSet<PersonDTO>();
}
}
Is there a way to map these Domain Entities to their DTO's? The problem is that PersonDTO needs a collection of ClubDTO, not just Club, and vice versa for ClubDTO needing a collection of PersonDTO and not just Person.
This design causes an infinite loop when trying to map PersonClub -> PersonDTO and PersonClub -> ClubDTO:
Mapper.CreateMap<Person, PersonDTO>();
Mapper.CreateMap<Club, ClubDTO>();
Mapper.CreateMap<PersonClub, ClubDTO>()
.ConvertUsing(x => Mapper.Map<Club, ClubDTO>(x.Club));
Mapper.CreateMap<PersonClub, PersonDTO>()
.ConvertUsing(x => Mapper.Map<Person, PersonDTO>(x.Person));
I understand why this is happening, but am curious to how others handle this situation.
In this situation when calling Mapper.Map<Person, PersonDTO>(personEntity), it isn't necessary to load all members of all clubs that a person is a part of (The relationship doesn't need to go that deep ever). Same is true for Mapper.Map<Club, ClubDTO>(clubEntity).
Is this a design flaw? Would it be better to not have a PersonClub domain entity and just have public virtual ISet<Club> Clubs { get; set; } as the ManyToMany relationship? Although I believe this would still cause the circular reference.
Any input is appreciated.
Related
This is done using MVC .net framework and entity framework "database first" approach. There is a many to many relationship between two tables. They are connected through third table that has combined key as id from first table and id from second table.
public class ManyToManyTable
{
[Required]
[Key, Column(Order=0)]
public int firsttableid { get; set; }
[Required]
[Key, Column(Order=1)]
public int secondtableid { get; set; }
public int something { get; set; }
[ForeignKey("firsttableid")]
public virtual FirstTable firstTable { get; set; }
[ForeignKey("secondtableid")]
public virtual SecondTable secondTable { get; set; }
}
First and Second table have some id which is primary key.
I want to create View and Controller method that enables master detail entry form for this ManyToManyTable. that would have FirstTable in Master and SecondTAble in details, and all to be saved in ManyToManyTable when button Save is pressed.
Of course, both First and Second Table have this property:
public virtual ICollection<ManyToManyTable> ManyToManyTables { get; set; }
What is the easiest way to implement cases like this one?
Thank you!
EF has a default conventions for many-to-many relationships. No need to create specific
mapping class. You have to include navigation properties in both "FirstTable" and "SecondTable" Class as shown below.
public class FirstTable
{
public FirstTable()
{
secondTableProperties = new HashSet<SecondTable>();
}
public int Id { get; set; }
public int MyProperty2 { get; set; }
public int MyProperty3 { get; set; }
public virtual ICollection<SecondTable> secondTableProperties { get; set; }
}
public class SecondTable
{
public SecondTable()
{
FirstTableProperties = new HashSet<FirstTable>();
}
public int Id { get; set; }
public int MyProperty2 { get; set; }
public int MyProperty3 { get; set; }
public virtual ICollection<FirstTable> FirstTableProperties { get; set; }
}
Remove mapping class from DBContext , only include above two classes. Build and run the application , EF will automatically create a Mapping table in SQL server. Usually the Mapping table contains only the primary keys of other two tables.
You can use Fluent API to take some control on the created mapping table
modelBuilder.Entity<FirstTable>()
.HasMany<SecondTable>(s => s.FirstTableProperties)
.WithMany(c => c.secondTableProperties)
.Map(cs =>
{
cs.MapLeftKey("FirstTableId");
cs.MapRightKey("SecondTableId");
cs.ToTable("ManyToManyTable");
});
If you want to work with a join table with additional properties, above mentioned many-to-many relationship won't work . In that case you will have to create two one-to-many relationships as shown below.
public class FirstTable
{
public int Id { get; set; }
public int MyProperty2 { get; set; }
public virtual ICollection<ManyToManyTable> manytomany { get; set; }
}
public class SecondTable
{
public int Id { get; set; }
public int MyProperty2 { get; set; }
public virtual ICollection<ManyToManyTable> manytomany { get; set; }
}
public ManyToManyTable
{
[Required]
[Key, Column(Order=0)]
public int firsttableid { get; set; }
[Required]
[Key, Column(Order=1)]
public int secondtableid { get; set; }
public int AdditionalProperty { get; set; }
public virtual FirstTable first { get; set; }
public virtual SecondTable Second { get; set; }
}
I am not able to fetch the Centers Location where as I am able to save and update it it database. Its only fetching where I am facing problem
public class Club
{
public Club()
{
this.Memberships = new HashSet<Membership>();
this.People = new HashSet<Manager>();
this.Center = new Center();
}
public int ClubId { get; set; }
public string ClubName { get; set; }
public System.DateTime OpenDate { get; set; }
[ForeignKey("Center")]
public virtual Center Center { get; set; }
public virtual ICollection<Membership> Memberships { get; set; }
public virtual ICollection<Manager> People { get; set; }
}
My center model
public class Center
{
[Key,ForeignKey("Club")]
public int ClubId { get; set; }
public string Location { get; set; }
public virtual Club Club { get; set; }
}
and the index method is
public ActionResult Index()
{
return View(db.Clubs.ToList());
}
Your reference to Center is virtual, so it is lazy loaded. Try this instead:
return View(db.Clubs.Include(c => c.Center).ToList());
https://msdn.microsoft.com/en-us/data/jj574232.aspx#lazy
I noticed you're sending your entity model to the view. You may want to look into the viewmodel pattern and automapper. http://www.stevefenton.co.uk/Content/Blog/Date/201303/Blog/Why-You-Never-Expose-Your-Domain-Model-As-Your-MVC-Model/
I have those 2 Models
public class BranchEmployees
{
public int ID { get; set; }
[Required, Column(Order = 0), Key]
public string ApplicationUserID { get; set; }
[Required, Column(Order = 1), Key]
public int BranchID { get; set; }
public virtual ICollection<ApplicationUser> ApplicationUser { get; set; }
public virtual ICollection<Branch> Branch { get; set; }
}
public class Branch
{
public int ID { get; set; }
public string BranchName { get; set; }
[Required]
public string ApplicationUserID { get; set; }
public ApplicationUser User { get; set; }
public virtual ICollection<BranchEmployees> BranchEmployees { get; set; }
}
public class ApplicationUser
{
//rest of the code
}
UPDATE
I have everything set up but what I want is the query that gets me the Employees whose IDs are in the branch employees table
, I'm using entity framework code first with MVC 5 , how do I do it ?
Assuming that your ApplicationUser class will have a navigational property called BranchEmployees, here is the query that gets me the Employees whose IDs are in the branch employees table
List<ApplicationUsers> employeeNames =
dbContext
.ApplicationUsers
.Where(au => au.BranchEmployees
.Count() > 0).ToList();
Also, can you provide whole model including ApplicationUser? I also wonder why you do not prefer BranchEmployees to inherit from ApplicationUser.
You don't need a class that indicates a many-to-many relation between two tables when you do code-first. The key here is to create virtual properties of those classes. Lets say you have a class Student and class Course. Students can be in many Courses and Courses can have many Students. To generate a database using these models the classes should look like this:
public class Student
{
private ICollection<Course> _courses;
public Student()
{
this._courses = new HashSet<Course>();
}
[Key]
public int Id { get; set; }
public string FullName { get; set; }
public virtual ICollection<Course> Courses
{
get { return this._courses; }
set { this._courses = value; }
}
}
And for Course:
public class Course
{
private ICollection<Student> _students;
public Course()
{
this._students = new HashSet<Student>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Student> Students
{
get { return this._students; }
set { this._students = value; }
}
}
I hope that this can help you solve your issue.
Hi I'm trying to create a database in MVC containing a list of Tv shows and Actors associated with them.
Each Tv show can have multiple Actors and an actor can appear on many tv shows. Each actor has a cast name too, for each show they appear in. Here's my models.
public class TvShow
{
public int ShowId { get; set; }
public string Name { get; set; }
public List<Actor> cast { get; set; }
}
public class Actor
{
public int ActorId { get; set; }
public string Name { get; set; }
public List<TvShow> shows { get; set; }
}
public class Cast
{
public int ShowId { get; set; }
public int ActorId { get; set; }
public string CastName { get; set; }
}
public class TvContext : DbContext
{
public DbSet<TvShow> Shows { get; set; }
public DbSet<Actor> Actors { get; set; }
}
I query the database and run the application to create the database for me, but the CastName attribute is not appearing in my linker table. Any help would be greatly appreciated.
How can EF know that you want to use entity Cast as a M:N relation table?
You have to link entity Cast from TvShow and Actor entities when you want to have there an additional property on many to many relationship. So the model can look like this:
public class TvShow
{
public int ShowId { get; set; }
public string Name { get; set; }
public List<Cast> Casts { get; set; }
}
public class Actor
{
public int ActorId { get; set; }
public string Name { get; set; }
public List<Cast> Shows { get; set; }
}
public class Cast
{
public string CastName { get; set; }
public TvShow TwShow { get; set; }
public Actor Actor { get; set; }
}
And you can get list of actors for given TvShow with following query:
twShow.Casts.Select(c => c.Actor);
I am using MVC 4 with EF code first approach. I have two simple objects. These are their POCO classes:
public class Activity
{
//Primitive Properties
[HiddenInput]
public int Id { get; set; }
[Required]
public int LengthInMinutes { get; set; }
public string AdditionalInfo { get; set; }
[Required]
public bool Archive { get; set; }
//Navigation Properties
public virtual User User { get; set; }
public virtual ActivitySet ActivitySet { get; set; }
public virtual ICollection<Company> Companies { get; set; }
public virtual ICollection<Description> Descriptions { get; set; }
}
public class Company
{
//Primitive Properties
[HiddenInput]
public int Id { get; set; }
[Required]
public string Title { get; set; }
[Required]
public bool Archive { get; set; }
//Navigation Properties
public virtual ICollection<Activity> Activities { get; set; }
}
So there is a many-to-many relationship between Activity and Company entities. I am creating new Activity in my repository class, but when I assign a Company for the Activity like so:
activity.Companies.Add(company);
I get NullReference exception. I had a look around but according to this link:
BuildStarted.com
my approach seems to be right.
Why doesn't it work???
Before you can use Add() off Companies, it needs to be initialized.
activity.Companies = new List<Company>();
You could also initialize that in the contructor of Activity.
public class Activity
{
public Activity()
{
Companies = new List<Company>();
Descriptions = new List<Description>();
}
//Primitive Properties
[HiddenInput]
public int Id { get; set; }
[Required]
public int LengthInMinutes { get; set; }
public string AdditionalInfo { get; set; }
[Required]
public bool Archive { get; set; }
//Navigation Properties
public virtual User User { get; set; }
public virtual ActivitySet ActivitySet { get; set; }
public virtual ICollection<Company> Companies { get; set; }
public virtual ICollection<Description> Descriptions { get; set; }
}