Creating a Many-to-Many Relationship in EF4 Code First - asp.net-mvc-3

I am working on a project in ASP.NET MVC using EF4 Code-First for the modeling. I have the following model classes:
public class ComicBook
{
public long ComicBookID { get; set; }
public string IssueTitle { get; set; }
public int IssueNumber { get; set; }
public DateTime PublishDate { get; set; }
public IList<ComicBookPerson> Writers { get; set; }
public IList<ComicBookPerson> Pencillers { get; set; }
public IList<User> CollectingUsers { get; set; }
}
public class ComicBookPerson
{
public long ComicBookPersonID { get; set; }
public string Name { get; set; }
public IList<ComicBook> WorkedOnComicBooks { get; set; }
}
public class User
{
[Key]
public long UserID { get; set; }
public string Email { get; set; }
public IList<ComicBook> CollectedBooks { get; set; }
}
And the following DbContext:
public class ComicContext : DbContext
{
public DbSet<ComicBook> ComicBooks { get; set; }
public DbSet<ComicBookPerson> ComicBookPerson { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ComicBook>()
.HasMany(b => b.Writers)
.WithMany(w => w.WorkedOnComicBooks)
.Map(t => t.MapLeftKey("ComicBookID")
.MapRightKey("WriterID")
.ToTable("ComicBook_Writers"));
modelBuilder.Entity<ComicBook>()
.HasMany(b => b.Pencillers)
.WithMany(p => p.WorkedOnComicBooks)
.Map(t => t.MapLeftKey("ComicBookID")
.MapRightKey("PencillerID")
.ToTable("ComicBook_Penciller"));
}
}
The rules that need to apply are:
Users can have many ComicBooks in their collection
ComicBooks can be in many users' collections
Each ComicBook can have 1 or more Writers
Each ComicBook can have 1 or more Pencillers
A ComicBookPerson can be a Writer or a Penciller on any book
A ComicBookPerson can work on many books, as a Writer, Penciller, or both
The many-to-many schema between Users and ComicBooks is created fine. It is the many-to-many between ComicBooks and ComicBookPersons that is breaking. The error message I get is:
Schema specified is not valid. Errors:
(15,6) : error 0040: Type ComicBook_Writers is not defined in namespace Comics.Models (Alias=Self).
I basically want two join tables, one called ComicBook_Writers and one called ComicBookPencillers. Both tables will contain a ComicBookID and a ComicBookPersonID. Is this possible in EF Code First?

You need two collection properties in ComicBookPerson class that relates to writers and pencillers.
public class ComicBookPerson
{
public long ComicBookPersonID { get; set; }
public string Name { get; set; }
public IList<ComicBook> WroteComicBooks { get; set; }
public IList<ComicBook> PenciledComicBooks { get; set; }
}
Model mapped as
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ComicBook>()
.HasMany(b => b.Writers)
.WithMany(w => w.WroteComicBooks)
.Map(t => t.MapLeftKey("ComicBookID")
.MapRightKey("WriterID")
.ToTable("ComicBook_Writers"));
modelBuilder.Entity<ComicBook>()
.HasMany(b => b.Pencillers)
.WithMany(p => p.PenciledComicBooks)
.Map(t => t.MapLeftKey("ComicBookID")
.MapRightKey("PencillerID")
.ToTable("ComicBook_Penciller"));
}

I think this model is close to be right, but still missing something.
The ComicBookPerson can be both writer and penciller, that means you still need a third list in your class ComicBookPerson:
public IList<ComicBook> WroteComicBooks { get; set; }
public IList<ComicBook> PenciledComicBooks { get; set; }
public IList<ComicBook> WrotPencComicBooks { get; set; }
Am I right?

Related

How do I define this relationship with code-first?

I'm trying to create a library of books.
I separated data and users context/database for security reasons (and clarity) but is it that useful?
Specially since BookOfUserEntity.Id should be really a composite key between UserId & BookId.
Something like :
public class BookOfUserEntity
{
[Key]
public Guid UserId { get; set; }
[Key]
public int BookId { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<BookOfUserEntity>()
.HasKey(b => new { b.UserId, b.BookId });
}
But then if I create a composite key what should be HistoryEntity.BookOfUserEntityId type & value?
I also separated BookEntity & BookOfUserEntity to avoid duplicated data and reuse what can be reused (MainCoverArtId etc...), but this added complexity - should I go back to a simpler model?
public class BookEntity
{
public int Id { get; set; }
public Guid? MainCoverArtId { get; set; }
...
public virtual List<BookOfUserEntity>? BookOfUserEntities { get; set; } // Only purpose is to know how many Users have this very book in their list.
}
public class BookOfUserEntity // Bad name here don't mention it
{
public int Id { get; set; }
public Guid UserId { get; set; }
public int BookId { get; set; }
public BookEntity Book { get; set; } // Useful? Should be virtual?
public List<HistoryEntity>? HistoryEntities { get; set; }
...
}
public class HistoryEntity
{
public int Id { get; set; }
public int BookOfUserEntityId { get; set; }
public BookOfUserEntity BookOfUserEntity { get; set; } // Same questions
public int Vol { get; set; }
public double Chapter { get; set; }
public DateTime? ReadDate { get; init; }
}

Why Entity Framework replaces provided value with a new incremental value

I'm trying to work my way through a Dot Net Core MVC project, but I am facing a problem I couldn't solve.
And honestly, I didn't know what to search for.
The project is a simple vehicle registry,
Each vehicle has a Make, a Model, a some Features.
Here are the domain models:
public class Make
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Model> Models { get; set; }
public Make()
{
Models=new Collection<Model>();
}
}
public class Model
{
public int Id { get; set; }
public string Name { get; set; }
public Make Make { get; set; }
public int MakeId { get; set; }
public Model()
{
Make=new Make();
}
}
public class Feature
{
public int Id { get; set; }
public string Name { get; set; }
}
[Table(name:"VehicleFeatures")]
public class VehicleFeature
{
public int VehicleId { get; set; }
public int FeatureId { get; set; }
public Feature Feature { get; set; }
public Vehicle Vehicle { get; set; }
public VehicleFeature()
{
Feature=new Feature();
Vehicle=new Vehicle();
}
}
public class Vehicle
{
public int Id { get; set; }
public Model Model { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ModelId { get; set; }
public ICollection<VehicleFeature> VehicleFeatures { get; set; }
public string ContactName { get; set; }
public string ContactPhone { get; set; }
public string ContactEmail { get; set; }
public DateTime LastUpdate { get; set; }
public bool IsRegistered { get; set; }
public Vehicle()
{
Model=new Model();
VehicleFeatures=new Collection<VehicleFeature>();
}
}
The problem is that when I send the following request to the corresponding controller, EF is replacing the provided value with a new incremental value.
The request I send:
{
"ModelId":10,
"VehicleFeatures":[
{"featureId":45},
{"featureId":46}
],
"ContactName":"Alice",
"ContactPhone":"1234",
"ContactEmail":"Alice#local",
"LastUpdate":"1980-01-01",
"IsRegistered":true
}
And this is what happens:
The controller receives correct values,
Correct values are added to the context,
And then suddenly all hell breaks loose.
This is the controller code:
[HttpPost]
public async Task<IActionResult> CreateVehicle([FromBody] Vehicle v)
{
context.Vehicles.Add(v);
await context.SaveChangesAsync();
return Ok(v.ModelId);
}
What am I missing here?

Querying Many to Many relationship table to entities MVC

I have many to many relationship tables. Sube and User
namespace Odev.Entities
{
public class User
{
[Key]
public int Id { get; set; }
[Required]
[DisplayName("Kimlik No")]
[ StringLength(11)]
[Index(IsUnique = true)]
public string UserName { get; set; }
[ StringLength(8)]
[DisplayName("Şifre")]
public string Password { get; set; }
[DisplayName("Ad Soyad")]
public string NameSurname { get; set; }
[DisplayName("Bölüm")]
public string Bolum { get; set; }
[DisplayName("Dal")]
public string Dal { get; set; }
[DisplayName("Öğrenci No")]
public int OgrenciNo { get; set; }
public int RoleId { get; set; }
public virtual ICollection<Sube> Subes { get; set; }
public virtual List<Notification> Notifications { get; set; }
public virtual List<Homework> Homeworks { get; set; }
public virtual Role Role { get; set; }
}
}
namespace Odev.Entities
{
public class Sube
{
[Key]
public int Id { get; set; }
[DisplayName("Şube")]
public string Sube_Name { get; set; }
public virtual Homework Homework { get; set; }
public virtual ICollection<User> Users { get; set; }
}
}
namespace Odev.DataAccessLayer
{
public class DatabaseContext : DbContext
{
public DatabaseContext() : base("dataConnection")
{
Database.SetInitializer(new OdevInitializer());
}
public DbSet<User> Users { get; set; }
public DbSet<Homework> Homeworks { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<Sube> Subes { get; set; }
public DbSet<Notification> Notifications { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
}
and my ViewModel
namespace Odev.Models{
public class ViewModel{
public string Sube_Name { get; set; }
public string NameSurname { get; set; }
public string UserName { get; set; }
public int UserId { get; set; }
public int SubeId { get; set; }
public User User { get; set; }
public Sube Sube { get; set; }
public Role Role { get; set; }
}}
I want to show User's SubeId:
How I select Sube's id ?
You need connect both entities with a foreign key.
Please, read this doc: Creating a More Complex Data Model for an ASP.NET MVC Application

Data Annotation for foreign key relationship

I have two classes
public class Project
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public int ManagerID { get; set; }
public int CoordID { get; set; }
[ForeignKey("ManagerID")]
public virtual Employee Manager { get; set; }
[ForeignKey("CoordID")]
public virtual Employee Coord { get; set; }
}
public class Employee
{
[Key]
public int EmpID { get; set; }
public string Name { get; set; }
[InverseProperty("ManagerID")]
public virtual ICollection<Project> ManagerProjects { get; set; }
[InverseProperty("CoordID")]
public virtual ICollection<Project> CoordProjects { get; set; }
}
The ManagerID and CoordID map to the EmpID column of the Employee table.
I keep getting an error for Invalid Columns becauce EF is not able to map correctly. I think it is looking for wrong column.
I think InverseProperty is used to refer to the related navigation property, not the foreign key, e.g.
public class Employee
{
[Key]
public int EmpID { get; set; }
public int Name { get; set; }
[InverseProperty("Manager")]
public virtual ICollection<Project> ManagerProjects { get; set; }
[InverseProperty("Coord")]
public virtual ICollection<Project> CoordProjects { get; set; }
}
Also, is there a reason why your names are ints and not strings?
Best guess would be to use fluent API in your context via OnModelCreating. By renaming the column, EF can't figure out the original object to map so it's confused. However, Fluent API allows you to manually specify the map using something like the following:
public class MyContext : DbContext
{
public DbSet<Employee> Employees { get; set; }
public DbSet<Project> Projects { get; set; }
protected override OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Project>()
.HasRequired(x => x.Manager)
.WithMany(x => x.ManagerProjects)
.HasForeignKey(x => x.ManagerID);
modelBuilder.Entity<Project>()
.HasRequired(x => x.Coord)
.WithMany(x => x.CoordProjects)
.HasForeignKey(x => x.CoordID);
}
}

Entity Framework multiple relationships between tables

I've have two tables in my project which are User and InvoiceLine.
It has been specified that an InvoiceLine can have a User known as a Checker.
My models are:
public class InvoiceLine : IEntity
{
public virtual int Id { get; set; }
public virtual int? CheckerId { get; set; }
public virtual string CreatedByUserName { get; set; }
public virtual DateTime CreatedDateTime { get; set; }
public virtual string LastModifiedByUserName { get; set; }
public virtual DateTime? LastModifiedDateTime { get; set; }
// Navigation properties
public virtual User Checker{ get; set; }
}
public class User : IEntity
{
public int Id { get; set; }
public string CreatedByUserName { get; set; }
public DateTime CreatedDateTime { get; set; }
public string LastModifiedByUserName { get; set; }
public DateTime? LastModifiedDateTime { get; set; }
//Navigation properties
public virtual ICollection<InvoiceLine> InvoiceLines { get; set; }
}
So this was fine I have a 0..1 to many relationship from User to InvoiceLine.
This meant with Linq I could get the InvoiceLines the User needs to check via:
user.InvoiceLines
However there is another requirement that an InvoiceLine also has an Auditor so I modified the InvoiceLine to:
public class InvoiceLine : IEntity
{
public virtual int Id { get; set; }
public virtual int? CheckerId { get; set; }
public virtual int? AuditorId { get; set; }
public virtual string CreatedByUserName { get; set; }
public virtual DateTime CreatedDateTime { get; set; }
public virtual string LastModifiedByUserName { get; set; }
public virtual DateTime? LastModifiedDateTime { get; set; }
// Navigation properties}
public virtual User Checker { get; set; }
public virtual User Auditor { get; set; }
}
So what I was really wanting was to go:
user.InvoiceLines
and get the Checkers and Auditors or alternatively get them seperately via:
user.CheckerInvoiceLines
user.AuditorInvoiceLines
I'm getting null back from user.InvoiceLines though which is understandable.
Could someone please point me in the right direction on how to use Linq to get the InvoiceLines from the User?
Edit Update:
My model configuration code is like:
public class VectorCheckContext : DbContext
{
...
public DbSet<InvoiceLine> InvoiceLines { get; set; }
public DbSet<User> Users { get; set; }
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
}
You need to use fluent mappings to configure the relationships when EF can not resolve them by conventions.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
//other mappings
modelBuilder.Entity<InvoiceLine>()
.HasOptional(i => i.Checker)
.WithMany(u => u.CheckerInvoiceLines)
.HasForeignKey(i => i.CheckerId);
modelBuilder.Entity<InvoiceLine>()
.HasOptional(i => i.Auditor)
.WithMany(u => u.AuditorInvoiceLines)
.HasForeignKey(i => i.AuditorId);
}

Resources