Is it possible to do the following mapping with the Entity Framework Code First?
public class Parent
{
[Key]
public int ID {get; set;}
public string statecode{get; set;}
public virtual ICollection<Child> children{get; set;}
}
public class Child
{
[Key, Column(Order = 0)]
public string StateFromCode { get; set; }
[Key, Column(Order = 1)]
public string StateToCode { get; set; }
}
I would like to specify a relation in my dbcontext where children are lazy loaded into the Parent and Parent.statecode == Child.StateToCode. Note that StateToCode is part of the Child key and not the entire primary key. What should I specify in my DB context below to make this happen if possible at all?
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Parent>().HasMany(x=>x.children)..............
}
It is not possible - especially not with code first where relations must follow exactly same rules as specifying referential constraint in the database. So the only valid relation will require Parent's Id column in Child table and foreign constraint on this column
Related
I'm using EntityFramework 6. I would like to split my DbContext into multiple smaller ones. Edit: I have a single database I wish to connect to.
I will have a single DbContext with all the entities and migrations that only one central application will use. Other applications will use the smaller DbContext without migrations.
I have found that I need to inherit all DbContext from IdentityDbContext, even if they use none of it.
internal class SmallerDbContext : IdentityDbContext<ApplicationUser,
ApplicationRole, int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
public DbContext<Entity1> NotInSmallerDbContext { get; set; }
public DbContext<Entity1> InSmallerDbContext { get; set; }
}
I would like this to be:
internal class SmallerDbContext : System.Data.Entity.DbContext
{
public DbContext<Entity1> InSmallerDbContext { get; set; }
}
But this yields the following errors:
Paris.DAL.Repo.DbContext.ApplicationUserLogin: : EntityType 'ApplicationUserLogin' has no key defined. Define the key for this EntityType.
Paris.DAL.Repo.DbContext.ApplicationUserRole: : EntityType 'ApplicationUserRole' has no key defined. Define the key for this EntityType.
ApplicationUserLogins: EntityType: EntitySet 'ApplicationUserLogins' is based on type 'ApplicationUserLogin' that has no keys defined.
ApplicationUserRoles: EntityType: EntitySet 'ApplicationUserRoles' is based on type 'ApplicationUserRole' that has no keys defined.
I have tried to add these tables to the DbContext, but to no avail.
public IDbSet<ApplicationUser> Users { get; set; }
public IDbSet<ApplicationRole> Roles { get; set; }
Is there any way I can create DbContext that does not inherit from IdentityDbContext?
I was on the right path by adding the objects to the DbContext.
You need to add only two, the User and UserLogin. The others are not needed.
public DbSet<ApplicationUser> Users { get; set; }
public DbSet<ApplicationUserLogin> UserLogins { get; set; }
Additionally, you need to set the correct table for both objects and you need to add some of virtual properties so that Entity Framework can handle these types as it would any of your other classes.
[Table("AspNetUsers")]
public class ApplicationUser : IdentityUser<int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
}
[Table("AspNetUserLogins")]
public class ApplicationUserLogin : IdentityUserLogin<int>
{
[Key, Column(Order = 0)]
public override string LoginProvider { get; set; }
[Key, Column(Order = 1)]
public override string ProviderKey { get; set; }
[Key, Column(Order = 2)]
public override int UserId { get; set; }
}
Now, your DbContext no longer has to inherit from IdentityDbContext.
I have two models, One ApplicationUser which holds all users in the system and I have a Quotation model which will hold all Quotations made. now I want to store two mappings to ApplicationUser inside Quotations. So that I can map to created User as well as cancelled User. My model looks like this
public class Quotation
{
public int QuotationID { get; set; }
public DateTime QuotationDate { get; set; }
public DateTime QuotationCancelDate { get; set; }
public int ApplicationUserID { get; set; }
public virtual ApplicationUser CreatedUser { get; set; }
[ForeignKey("ApplicationUserID")]
public ApplicationUser CancelledUser { get; set; }
}
But this throws an error
Quotation_CancelledUser_Target_Quotation_CancelledUser_Source: : The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'ApplicationUserID' on entity 'Quotation' does not match the type of property 'Id' on entity 'ApplicationUser' in the referential constraint 'Quotation_CancelledUser'.
So I guess , The approach I am taking is wrong. Can anyone point out the correct way to achieve this?
The problem you are observing is called "Multiple Cascade Path". A Multiple Cascade Path happens when a cascade path goes from column col1 in table A to table B and also from column col2 in table A to table B.
The exception is caused by SQL Server when code first attempted to add table that has columns appearing more than once of another table.
In SQL Server, a table cannot appear more than one time in a list of all the cascading referential actions that are started by either a DELETE or an UPDATE statement. For example, the tree of cascading referential actions must only have one path to a particular table on the cascading referential actions tree.
You will need to use FluentAPI to configure the relationship. I am using EF5 currently and do not know if this can be accomplished in EF6/7.
So modifying your code sample, it would look like:
public class Quotation
{
public int QuotationID { get; set; }
public DateTime QuotationDate { get; set; }
public DateTime QuotationCancelDate { get; set; }
public int CreatedUserID { get; set; }
// Navigation property
public virtual ApplicationUser CreatedUser { get; set; }
public int CancelledUserID { get; set; }
// Navigation property
public virtual ApplicationUser CancelledUser { get; set; }
}
// Created a simple class for example
public class ApplicationUser
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
Now in you context class you can write:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Disable the default PluralizingTableNameConvention
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
// Add configuration here
modelBuilder.Entity<Quotation>()
.HasKey(e => e.QuotationID);
modelBuilder.Entity<ApplicationUser>()
.HasKey(e => e.Id);
modelBuilder.Entity<Quotation>()
.HasRequired(a => a.CreatedUser)
.WithMany()
.HasForeignKey(u => u.CreatedUserID);
modelBuilder.Entity<Quotation>()
.HasRequired(a => a.CancelledUser)
.WithMany()
.HasForeignKey(u => u.CancelledUserID);
}
For more information with example refer this link.
I am using an existing database with EF Code First and using the modelBuilder to configure. I have two tables where a SESSION can have a SUBJECT, classes are as such:
public class SessionItem {
[Key]
public int SessionId { get;set; }
// Other Values
public int Subject_ID { get;set; }
public virtual Subject Subject { get;set; }
}
public class SubjectItem {
[Key]
public int Subject_ID { get;set; }
// Other Values
public virtual SessionItem Session { get;set; }
}
And then the modelBuilder code is:
modelBuilder.Entity<SessionItem>().ToTable("tblTblSessions");
modelBuilder.Entity<Subject>().ToTable("tblTblSubjects");
modelBuilder.Entity<SessionItem>()
.HasOptional<Subject>(u => u.Subject)
.WithOptionalDependent(c => c.Session).Map(p => p.MapKey("Subject_ID"));
This failed at first until I removed Subject_ID from the SessionItem class, then I got the error: A relationship multiplicity constraint violation occurred: An EntityReference can have no more than one related object, but the query returned more than one related object. This is a non-recoverable error.
Any idea where I have gone wrong?
Unfortunely one-to-one foreign key associations are not supported with Entity Framework because EF doesn't know what a unique key constraint is (that your Subject_ID column in the Session table apparently has).
You must workaround this by mapping the relationship as one-to-many. Follow the mapping in #flem's answer for the SessionItem entity and for the SubjectItem entity either remove the public virtual SessionItem Session { get;set; } altogether or replace it by
public virtual ICollection<SessionItem> Sessions { get; set; }
You don't need the mapping with Fluent API anymore for this relationship, or if you want, it should be:
modelBuilder.Entity<SessionItem>()
.HasOptional(se => se.Subject)
.WithMany() // or WithMany(su => su.Sessions)
.HasForeignKey(se => se.Subject_ID);
When you add items to that collection you must ensure in your business logic that you don't add more than one item because you can't have more than one row with the same Subject_ID in your database due to the unique key constraint. When you load a subject from the database including the sessions the session collection is either empty or has one single element, but not more.
Try this:
public class SessionItem
{
[Key]
public int SessionId { get;set; }
// Other Values
[ForeignKey("Subject")]
public int? Subject_ID { get;set; }
[ForeignKey("Subject_ID")]
public virtual SubjectItem Subject { get;set; }
}
You need to make Subject_ID foreign key nullable.
[Table("tblTblSessions")]
public class SessionItem {
[Key]
public int SessionId { get; set; }
public virtual SubjectItem Subject { get; set; }
}
[Table("tblTblSubjects")]
public class SubjectItem {
[Key, ForeignKey("Session")]
public int Subject_ID { get; set; }
public virtual SessionItem Session { get; set; }
}
One-to-one relationship between SubjectItem and SessionItem, and you can get rid of all your modelBuilder code - all the table naming and one-to-one mapping you were doing is taken care of with the attributes and properties above.
Edit: Fixed a typo and marked the Dependent side of the one-to-one.
I have a class with a relationship to another table.
public class MyClass
{
[Key]
public Guid Id {get; set; }
public virtual OtherClass OtherClass { get; set; }
}
I hook this up to a controller and create views for CRUD - all works fine.
In the DB a OtherClass_OtherClassId column is created, but this is not in the model.
How can I put a reference in this Id column during the controller's Create method?
How can I force this relationship to be [Required] without having to create a brand new OtherClass each time?
Annotated class with some description:
public class MyClass
{
// [Key] - Don't actually need this attribute
// EF Code First has a number of conventions.
// Columns called "Id" are assumed to be the Key.
public Guid Id {get; set; }
// This reference creates an 'Independent Association'. The Database
// foreign key is created by convention and hidden away in the code.
[Required]
public virtual OtherClass OtherClass { get; set; }
// This setup explicitly declares the foreign key property.
// Again, by convention, EF assumes that "FooId" will be the key for
// a reference to object "Foo"
// This will still be required and a cascade-on-delete property
// like above - an int? would make the association optional.
public int OtherClass2Id { get; set; }
// Leave the navigation property as this - no [Required]
public virtual OtherClass2 { get; set; }
}
So which is better? Independent associations or declaring the foriegn key?
Independent associations match object programming closer. With OOP, one object doesn't really care much about the Id of a member. ORM's try to cover these relationships up, with varying degrees of success.
Declaring the foreign key puts database concerns into your model, but there are scenarios where this makes dealing with EF much easier.
Example - when updating an object with a required independent association, EF will want to have the entire object graph in place.
public class MyClass
{
public int Id {get; set; }
public string Name { get; set; }
[Required] // Note the required. An optional won't have issues below.
public virtual OtherClass OtherClass { get; set; }
}
var c = db.MyClasses.Find(1);
c.Name = "Bruce Wayne";
// Validation error on c.OtherClass.
// EF expects required associations to be loaded.
db.SaveChanges();
If all you want to do is update the name, you'll either have to pull OtherClass from the database as well since it's required for entity validation or attach a stubbed entity (assuming you know the id). If you explicitly declare foreign key, then you won't run into this scenario.
Now with foreign keys, you run into a different issue:
public class MyClass
{
public Guid Id {get; set; }
public string Name { get; set; }
public int OtherClassId { get; set }
public virtual OtherClass OtherClass { get; set; }
}
var c = db.MyClasses.Find(1);
// Stepping through dubugger, here, c.OtherClassId = old id
c.OtherClass = somethingElse;
// c.OtherClassId = old id - Object and id not synced!
db.SaveChanges();
// c.OtherClassId = new id, association persists correctly though.
In summary -
Independent associations
Good: Match OOP and POCO's better
Bad: Often requires a full object graph, even if you're only updating one or two properties. More EF headaches.
Foreign Keys
Good: Easier to work with sometimes - less EF headaches.
Bad: Can be out of sync with their object
Bad: Database concerns in your POCO's
EF generally require handholding with the model configuration. This should get you started. However doing a good tutorial on EF Code First and DB first would be greatly beneficial.
Following has:
Order with multiple OrderItems
single User
and single OrderType made by keeping the identity OrderTypeId and the actual OrderType ref object.
public class Order
{
public Order()
{
OrderItems = new OrderItemCollection();
}
public int OrderID { get; set; }
public DateTime OrderDate { get; set; }
public string OrderName { get; set; }
public int UserId { get; set; }
public virtual User OrderUser { get; set; }
public virtual OrderItemCollection OrderItems { get; set; }
public int? OrderTypeId { get; set; }
public OrderType OrderType { get; set; }
public override int GetHashCode() { return OrderID.GetHashCode();}
}
public class OrderConfiguration : EntityTypeConfiguration
{
public OrderConfiguration()
{
this.ToTable("ORDERS");
this.HasKey(p => p.OrderID);
this.Property(x => x.OrderID).HasColumnName("ORDER_ID");
this.Property(x => x.OrderName).HasMaxLength(200);
this.HasMany(x => x.OrderItems).WithOptional().HasForeignKey(x => x.OrderID).WillCascadeOnDelete(true);
this.HasRequired(u => u.OrderUser).WithMany().HasForeignKey(u => u.UserId);
this.Property(x => x.OrderTypeId).HasColumnName("ORDER_TYPE_ID");
this.HasOptional(u => u.OrderType).WithMany().HasForeignKey(u => u.OrderTypeId);
}
}
public class OrderContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new OrderConfiguration());
}
}
'
I'm using CTP5 of the code first. Take the following simple class and associated DbContext
public class Test
{
public Test()
{
Keywords = new Collection<string>();
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<string> Keywords { get; set; }
}
public class TestDbContext : DbContext
{
public TestDbContext()
{
Database.Delete();
Database.CreateIfNotExists();
}
public DbSet<Test> Tests { get; set; }
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
The type 'System.Collections.Generic.ICollection' must be a non-nullable value type in order to use it as parameter 'T'
in the generic type or method 'System.Data.Entity.ModelConfiguration.Configuration.Types.StructuralTypeConfiguration
.Property(System.Linq.Expressions.Expression>)'
Any ideas how to create the necessary table relationships between test class and the keywords property?
Add class Keyword and change ICollection<string> Keywords {get; set;} to ICollection<Keyword> Keywords {get; set;}
public class Keyword
{
public string Data {get;set;}
}
and code model creating like this:
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Keyword>()
.HasKey(x => x.Data);
base.OnModelCreating(builder);
}
Just imagine, that you have 2 collections 1 - Keywords, 2 - Tags. Your realization would map it to same table, because in ORM mapping type defines entity. And both Keyword and Tag are of type string. So to split this entities you should split types.