I am trying to create a composite foreign key (if that is the correct term) which enforces a product class type of relationship on a table where the product is mandatory but class is not. I would like to use Code First and the Fluent API to achieve this model.
The scenario is:
I have commodities which can have many grades and tickets (deliveries) which are required to have a commodity and optionally a grade.
The problem I have is that with the model definition below the commodity/grade relationship is not enforced on tickets. So I can enter a ticket with a grade which is not related to the commodity specified on the ticket.
I have tried various Fluent statements with no success.
How do I create a foreign key which prevents an invalid Commodity Grade from being created in the tickets table?
I have the following model:
public class Commodity
{
public int CommodityId { get; set; }
...
public virtual ICollection<Grade> Grades { get; set; } = new HashSet<Grade>();
public virtual ICollection<Ticket> Tickets { get; set; } = new HashSet<Ticket>();
}
public class Grade
{
public int GradeId { get; set; }
...
public virtual Commodity Commodity { get; set; }
public virtual ICollection<Ticket> Tickets { get; set; } = new HashSet<Ticket>();
}
public class Ticket
{
public int TicketId { get; set; }
...
public virtual Commodity Commodity { get; set; }
public virtual Grade Grade { get; set; }
}
public class MyEntities : DbContext
{
public virtual DbSet<Commodity> Commodities { get; set; }
public virtual DbSet<Grade> Grades { get; set; }
public virtual DbSet<Ticket> Tickets { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Commodity>()
.HasMany(c => c.Grades)
.WithRequired(c => c.Commodity)
.WillCascadeOnDelete(true);
modelBuilder.Entity<Commodity>()
.HasMany(c => c.Tickets)
.WithRequired(t => t.Commodity)
.WillCascadeOnDelete(true);
modelBuilder.Entity<Grade>()
.HasMany(g => g.Tickets)
.WithOptional(t => t.Grade);
}
}
Thanks
Tony
Related
I have the following entities
public class Course
{
public long Id { get; set; }
public virtual ICollection<User> Users{ get; set; }
public virtual ICollection<UserCourse> CourseUsers { get; set; }
}
public class User
{
public long Id { get; set; }
public virtual ICollection<Course> Courses { get; set; }
public virtual ICollection<UserCourse> UserCourses { get; set; }
}
public class UserCourse
{
public long UserId { get; set; }
public User User { get; set; }
public long CourseId { get; set; }
public Course Course { get; set; }
public bool IsRequired { get; set; }
}
with the following mappings for
UserCourse mapping :
builder
.HasOne(nav => nav.User)
.WithMany(self => self.UserCourses)
.HasForeignKey(fk => fk.UserId)
.OnDelete(DeleteBehavior.Cascade);
builder
.HasOne(nav => nav.Course)
.WithMany(self => self.CourseUsers)
.HasForeignKey(fk => fk.CourseId)
.OnDelete(DeleteBehavior.Cascade);
and the User mapping
builder
.HasMany(nav => nav.Courses)
.WithMany(nav => nav.Users);
When trying to create a new migration I'm not exactly sure why I'm getting this.
Cannot use table 'UserCourse' for entity type 'UserCourse' since it is
being used for entity type 'UserCourse(Dictionary<string, object>)'
and potentially other entity types, but there is no linking
relationship. Add a foreign key to 'UserCourse' on the primary key
properties and pointing to the primary key on another entity typed
mapped to 'UserCourse'.
I understand what the error is, but not sure how to force the UserCourse mapping to use the User mapping generated join table or vice-versa
Also, I need the direcat mapping for OData, and the indirect mapping using the join entity to conduct operations on DbSet<UserCourse>
The public virtual ICollection<User> Users{ get; set; } in Course entity and the the public virtual ICollection<Course> Courses { get; set; } in Users entity are redundant. The entities should look more like this
public class Course
{
public long Id { get; set; }
public virtual ICollection<UserCourse> UserCourses { get; set; }
}
public class User
{
public long Id { get; set; }
public virtual ICollection<UserCourse> UserCourses { get; set; }
}
public class UserCourse
{
public long UserId { get; set; }
public User User { get; set; }
public long CourseId { get; set; }
public Course Course { get; set; }
}
And the OnModelCreating method should have this code
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserCourse>()
.HasKey(uc => new { uc.UserId, uc.CourseId });
modelBuilder.Entity<UserCourse>()
.HasOne(uc => uc.Course)
.WithMany(c => c.Users)
.HasForeignKey(uc => uc.CourseId);
modelBuilder.Entity<UserCourse>()
.HasOne(uc => uc.User)
.WithMany(c => c.Courses)
.HasForeignKey(uc => uc.UserId);
}
If you use EF core 5 you can directly skip the join table. It will be generated and handled by EF behind the scenes. More on the topic here https://www.thereformedprogrammer.net/updating-many-to-many-relationships-in-ef-core-5-and-above/
I'm trying to make an insert in a SQL database using Entity Framework 6 and I'm stuck on this issue that I cannot solve.
The error that I keep getting is :
UpdateException: Entities in 'Connect.CompanyFinancialDetails' participate in the 'Company_CompanyFinancialDetails' relationship. 0 related 'Company_CompanyFinancialDetails_Source' were found. 1 'Company_CompanyFinancialDetails_Source' is expected
I have these 2 entities:
public class Company
{
public long CUI { get; set; }
public string UserName { get; set; }
public string CompanyName { get; set; }
public string Symbol { get; set; }
public int? SharesCount { get; set; }
public decimal? SharePrice { get; set; }
public virtual Account Account { get; set; }
public virtual CompanyFinancialDetails CompanyFinancialDetails { get; set; }
}
public class CompanyFinancialDetails
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
// other properties
public decimal? NumberOfEmployees { get; set; }
public virtual Company Company { get; set; }
}
This is the Fluent API configuration:
public DbSet<Account> SignUpModels { get; set; }
public DbSet<Company> Companies { get; set; }
public DbSet<CompanyFinancialDetails> CompanyFinancialDetails { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Account>()
.HasKey(k => k.Id)
.HasOptional(s => s.Company)
.WithRequired(d => d.Account);
modelBuilder.Entity<Company>()
.HasKey(k => k.CUI)
.HasOptional(s => s.CompanyFinancialDetails)
.WithRequired(d => d.Company);
}
The relationship that I want to have is 1-many (one Company has many CompanyFinancialDetails).
This is the code where I add the objects to the database:
Company co = Context.Find(username);
foreach (CompanyFinancialDetails s in c)
{
s.Company = co;
}
a.CompanyFinancialDetails.AddRange(c);
a.SaveChanges();
I get a list of CompanyFinancialDetails and I add them using the AddRange method. I had this issue before and what I did was to add the virtual property object to the object that I wanted to insert in the database and it worked. This is what I tried to do here: the Find() method gets the company object that is related to the CompanyFinancialDetails and for each CompanyFinancialDetails object an Company virtual property is adding the related company object.
Well, it didn't work, when the SaveChanges() method is called, I get that error. Any help would be appreciated.
I am trying to establish foreign key to 2 classes using FluentAPI and bit confused on the way to implement it.
ApplicationUser uses ASP.NET Identity model and has UserId as string
public class ApplicationUser : IdentityUser
{
public virtual List<UserProduct> Orders { get; set; }
}
Product that has a composite key on columns ProductID and ProductCategoryID
public class Product
{
public int ProductID { get; set; }
public string ProductCategoryID { get; set; }
public virtual List<UserProduct> Orders { get; set; }
...
}
and another class UserProduct that will have many-to-many relationship between ApplicationUser and Product table
public partial class UserProduct
{
public string UserId { get; set; }
public int ProductID { get; set; }
public string ProductCategoryID { get; set; }
public virtual ApplicationUser User { get; set; }
public virtual Product Product { get; set; }
}
The FluentAPI code looks like
modelBuilder.Entity<Product>().Property(t => t.ProductID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Product>().HasKey(x => new { x.ProductID, x.ProductCategoryID });
modelBuilder.Entity<UserProduct>().HasKey(x => new {x.UserId, x.ProductID, x.ProductCategoryID});
How do I establish the foreign key relationship of UserProduct with ApplicationUser and Product?
You can add the id-property (e.g. OrderId) to the class UserProduct and use this code to connect entities by foreign key.
modelBuilder.Entity<UserProduct>()
.HasRequired(x => x.User)
.WithMany(u => u.Orders)
.HasForeignKey(x => x.OrderId);
Is it necessary to specify all the required relationship for mapping in the fluent API?
I have four classes in my model. To be specific: they are => BUS, PASSENGER, ROUTE, RESERVATION.
public class Bus
{
[Key]
public int Id { get; set; }
[Display(Name="Route")]
public int RouteId { get; set; }
..............
[ForeignKey("RouteId")]
public virtual Route Route{ get; set; }
public virtual ICollection<Reservation> Reservations { get; set; }
}
public class Passenger
{
public int Id { get; set; }
.........
.........
public virtual Reservation Reservation { get; set; }
}
public class Reservation
{
public int Id { get; set; }
public int BusId { get; set; }
public int PassengerId { get; set; }
.................
..............
[ForeignKey("BusId")]
public virtual Bus Bus { get; set; }
[ForeignKey("PassengerId")]
public virtual Passenger Passenger { get; set; }
}
public class Route
{
public int Id { get; set; }
.............
.............
public virtual ICollection<Bus> Busses { get; set; }
}
I have modeled my DbContext File as below:
public class TicketContext: DbContext
{
public TicketContext()
: base("TicketDbContext")
{
}
public DbSet<Bus> Busses { get; set; }
public DbSet<Passenger> Passengers { get; set; }
public DbSet<Reservation> Reservations { get; set; }
public DbSet<Route> Routes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure Code First to ignore PluralizingTableName convention
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
//configure the primary key for "Bus" ENTITY
modelBuilder.Entity<Bus>()
.HasKey(p=>p.Id);
//configure one to many relationship between "Bus" and "Passenger" ENTITY
modelBuilder.Entity<Bus>()
.HasMany(p => p.Reservations)
.WithRequired(p => p.Bus);
//configure the primary key for "Passenger" ENTITY
modelBuilder.Entity<Passenger>()
.HasKey(p => p.Id);
//configure one to one relationship between "Passenger" and "Reservation" ENTITY
modelBuilder.Entity<Passenger>()
.HasRequired(i => i.Reservation)
.WithRequiredPrincipal(i => i.Passenger);
//configure the primary key for "Reservation" ENTITY
modelBuilder.Entity<Reservation>()
.HasKey(p => p.Id);
//configure the primary key for "Route" ENTITY
modelBuilder.Entity<Route>()
.HasKey(p => p.Id);
//configure one to many relationship between "Route" and "Bus" ENTITY
modelBuilder.Entity<Route>()
.HasMany(r => r.Busses)
.WithOptional(r => r.Route);
}
}
After that when I wanted to create a controller for Bus with model class Bus and with the DbContext specified visual studio gives me the following error.
What have I done wrong here? Do I have to specify relationship for all entity?
I have problem because when I add the following to class Course I have only 2 tables not 3
public int PersonId { get; set; }
[ForeignKey("PersonId")]
public virtual Person Student { get; set; }
you do not have these three lines all good, but I need an additional field in class Course
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<Course> CoursesAttending { get; set; }
}
public class Course
{
public int CourseId { get; set; }
public string Title { get; set; }
public int PersonId { get; set; }
[ForeignKey("PersonId")]
public virtual Person Student { get; set; }
public virtual ICollection<Person> Students { get; set; }
}
public class SchoolContext : DbContext
{
public DbSet<Course> Courses { get; set; }
public DbSet<Person> People { get; set; }
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer<SchoolContext>(
new DropCreateDatabaseAlways<SchoolContext>());
SchoolContext db = new SchoolContext();
var cos = from d in db.Courses
select d;
}
}
please help me
EF cannot decide if Course.Student or Course.Students refers to Person.CoursesAttending. You must give EF a hint, for example by using the [InverseProperty] attribute on Course.Students:
[InverseProperty("CoursesAttending")]
public virtual ICollection<Person> Students { get; set; }
Edit
The model will cause multiple cascading delete paths, namely: When you delete a Person records in the join table will be deleted as well, but it will also delete all Courses that this person is assigned to through the Course.Person property. The deleted Courses will delete records in the join table again which is the second delete path on the same table.
Multiple cascading delete paths are not allowed with SQL Server, so you must disable it with Fluent API:
public class MyContext : DbContext
{
//...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>()
.HasRequired(c => c.Student)
.WithMany()
.HasForeignKey(c => c.PersonId)
.WillCascadeOnDelete(false);
}
}