How can I map an entity framework model to multiple tables?
How to perform insertion operation to specific table (by reference of string which stores the table name)?
I have not implemented this but a quick search provides many good examples of a practice known as Entity Splitting. The following should be useful:
http://www.c-sharpcorner.com/UploadFile/ff2f08/entity-splitting-in-entity-framework-6-code-first-approach/
public partial class Employee
{
// These fields come from the “Employee” table
public int EmployeeId { get; set; }
public string Code { get; set; }
public string Name { get; set; }
// These fields come from the “EmployeeDetails” table
public string PhoneNumber { get; set; }
public string EmailAddress { get; set; }
}
public partial class Model : DbContext
{
public Model() : base("name=EntityModel")
{
Database.Log = Console.WriteLine;
}
public virtual DbSet<Employee> Employees { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Employee>()
.Map(map =>
{
map.Properties(p => new
{
p.EmployeeId,
p.Name,
p.Code
});
map.ToTable("Employee");
})
// Map to the Users table
.Map(map =>
{
map.Properties(p => new
{
p.PhoneNumber,
p.EmailAddress
});
map.ToTable("EmployeeDetails");
});
}
}
All credit for the above code goes to linked post
In this case you can implement your own IModelCacheKeyFactory, which allow to hook into the model caching mechanism so EF is able to create different models based on some value right in runtime.
This article explains how
I have a Many To Many relationship with some additional fields. But as there are Photos added to the many to many relationship which might apply to other relations I wanted to seperate it so I can change it by just altering the One to many relation. This is the model
public class Segment
{
public int SegmentId { get; set; }
public int ConnectionPointIdEnd { get; set; }
public string ConnectionName { get; set; }
public string ConnectionInformation { get; set; }
public string Image { get; set; }
public string Direction { get; set; }
public ICollection<ConnectionPointRoute> ConnectionPointRoutes { get; set; }
}
public class ConnectionPointRoute
{
public int ConnectionPointId { get; set; }
public int RouteId { get; set; }
public int SegmentId { get; set; }
public int Position { get; set; }
public ConnectionPoint ConnectionPoint { get; set; }
public Route Route { get; set; }
public Segment Segment { get; set; }
}
And the modelbuilder looks like this :
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ConnectionPointRoute>()
.HasKey(c => new { c.ConnectionPointId, c.RouteId, c.SegmentId });
modelBuilder.Entity<ConnectionPoint>()
.HasMany(c => c.ConnectionPointRoutes)
.WithRequired(x => x.ConnectionPoint)
.HasForeignKey(c => c.ConnectionPointId);
modelBuilder.Entity<Route>()
.HasMany(c => c.ConnectionPointRoutes)
.WithRequired(x => x.Route)
.HasForeignKey(c => c.RouteId);
modelBuilder.Entity<Segment>()
.HasMany(c => c.ConnectionPointRoutes)
.WithRequired(x => x.Segment)
.HasForeignKey(c => c.SegmentId);
}
And this all works well for getting the items, but for some reason it doesn't allow me to post a new Route for instance, it gets me the error:
"Multiplicity constraint violated. The role
'Segment_ConnectionPointRoutes_Source' of the relationship
'InBuildingNavigator.Data.Models.Segment_ConnectionPointRoutes' has
multiplicity 1 or 0..1."
Any thoughts?
Fixed this! I had an error in my Post code, I added the full child objects which doesn't make a whole lot of sense in my case.
Ask me if you want a more detailed fix!
Just two more things to this:
I would recommend you to use an extra object for the many-to-many relationship (if you don't already do this). This will give you more control over the table name and over selections you may want to do.
use the virtual keyword for your properties, which you do not need directly (for your collections) - this will allow ef to implement lazy loading on them.
I am trying to make a self referencing table
public class Category
{
// PK
public int CategoryId { get; set; }
// Property
public string CategoryName { get; set; }
// FK
public int? ParentCategoryId { get; set; }
public virtual ICollection<Category> ParentCategories { get; set; }
public virtual ICollection<Product> Products { get; set; } // Product is defined later
}
and the configuration:
public class CategoryConfiguration : EntityTypeConfiguration<Category>
{
public CategoryConfiguration():base()
{
HasKey(c => new { c.CategoryId });
HasOptional(c => c.ParentCategories)
.WithMany()
.HasForeignKey(c => c.ParentCategoryId );
}
}
the idea is to use ParentCategoryId as the column name, but it's not working. Instead it generated a column named: Category_CategoryId.
I have tried to use .Map(c => c.MapKey("ParentCategoryId")) and the result was the same.
I don't think it is the reason of self referencing because the same thing happen in the many-to-many relationship :
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public virtual ICollection<Category> Categories { get; set; }
}
and the Product configuration
public class ProductConfiguration:EntityTypeConfiguration<Product>
{
public ProductConfiguration():base()
{
// Many-to-Many
HasMany(c => c.Categories)
.WithMany()
.Map(p =>
{
p.MapLeftKey("ProductRefId");
p.MapRightKey("CategoryRefId");
p.ToTable("ProductCategory");
});
}
}
The table name is ProductCategories instead of ProductCategory
The foreign key is Product_ProductId and Category_CategoryId
They all not what is expecting.
how can i solve the problem? Please help.
Thank you!
Update 1
strange thing is if I define it via DbModelBuilder, then it works
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Category>()
.HasOptional(c => c.ParentCategories)
.WithMany()
.HasForeignKey(c => c.ParentCategoryId);
}
the foreign key become ParentCategoryId as Expected.
Problem solved, I made a mistake. I didn't hook the configuration into DbContext.
by add these into the DbContext, the columns are renamed as expected.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new CategoryConfiguration());
modelBuilder.Configurations.Add(new ProductConfiguration());
}
I have the following entities:
[KnownType(typeof(Script))]
public class Application : IEntity
{
public long ID { get; set; }
public string Name { get; set; }
public long? StartScriptID { get; set; }
// Other properties...
// Navigation properties:
public virtual Script StartScript { get; set; } // new
public virtual List<Script> Scripts { get; set; }
}
[KnownType(typeof(Application))]
public class Script : IEntity
{
public long ID { get; set; }
public string Name { get; set; }
// Other properties...
// Navigation properties:
public virtual Application Application { get; set; }
}
I have no fluent configurations on the DbContext
So the model exists of Scripts which must be bound to an application. Applications can have a startScript defined. All this I already have working.
Now I am in need of a navigation property: Application.StartScript.
The question is how can I add the navigation property on Application without having to add a the equivalent navigation property on Script.
Thanks in advance!
EDIT: When I run Add-Migration the following migration code is generated:
public override void Up()
{
DropForeignKey("dbo.Scripts", "ApplicationID", "dbo.Applications");
AddColumn("dbo.Scripts", "Application_ID", c => c.Long());
CreateIndex("dbo.Applications", "StartScriptID");
CreateIndex("dbo.Scripts", "Application_ID");
AddForeignKey("dbo.Applications", "StartScriptID", "dbo.Scripts", "ID");
AddForeignKey("dbo.Scripts", "Application_ID", "dbo.Applications", "ID");
}
This creates a new column on Scripts which I don't need, as I already have the StartScriptID column.
EDIT: Update after #haim770 answer
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Application>().HasOptional(x => x.StartScript).WithOptionalDependent(x => x.Application);
}
public override void Up()
{
DropForeignKey("dbo.Scripts", "ApplicationID", "dbo.Applications");
DropIndex("dbo.Scripts", new[] { "ApplicationID" });
RenameColumn(table: "dbo.Applications", name: "ApplicationID", newName: "StartScript_ID");
AddColumn("dbo.Scripts", "Application_ID", c => c.Long());
CreateIndex("dbo.Applications", "StartScript_ID");
CreateIndex("dbo.Scripts", "Application_ID");
AddForeignKey("dbo.Scripts", "Application_ID", "dbo.Applications", "ID");
AddForeignKey("dbo.Applications", "StartScript_ID", "dbo.Scripts", "ID");
}
It doesn't understand that it should use the already existing StartScriptID. Is there a way to point it in the right direction?
EDIT: Wanted database structure:
Applications:
ID, PK, bigint, not null
Name, nvarchar(max), not null
StartScriptID, bigint, null
Scripts:
ID, PK, bigint, not null
Name, nvarchar(max), not null
ApplicationID, FK, bigint, not null
EDIT:
public class Application
{
[InverseProperty("StartScript")]
public long? StartScriptID { get; set; }
[ForeignKey("StartScriptID")]
public virtual Script StartScript { get; set; }
}
I was thinking there was no changes needed in the database, so I have tried adding migration with -IgnoreChanges. But then I got an EntityCommandExecutionException when querying for entities: "Invalid column name 'Application_ID'". So the Entity Framework needs some configuration for telling to use the StartScriptID property.
You can modify the mapping using the ForeignKeyAttribute and the InversePropertyAttribute.
If that doesn't work the way you want, you can always edit the migration yourself before applying it.
If you want to customize the column name, try the ColumnAttribute:
public class Application
{
[Column("DatabaseColumnName")]
public long? StartScriptID { get; set; }
[ForeignKey("StartScriptID")]
public virtual Script StartScript { get; set; }
}
A longer explanation can be found here: http://msdn.microsoft.com/de-de/data/gg193958.aspx
Try to use EntityTypeConfiguration It makes more easy to handle this kind of problem. Then:
public class Application
{
public long ID { get; set; }
public string Name { get; set; }
public long? StartScriptID { get; set; }
// Other properties...
// Navigation properties:
public virtual Script StartScript { get; set; } // new
public virtual ICollection<Script> Scripts { get; set; }
}
public class Script
{
public long ID { get; set; }
public string Name { get; set; }
public int ApplicationId { get; set; }
// Other properties...
// Navigation properties:
public virtual Application Application { get; set; }
}
public class ApplicationMap : EntityTypeConfiguration<Application>
{
public ApplicationMap()
{
this.HasKey(t => t.ID);
this.ToTable("TB_APLICATION", "aplication");
this.Property(t => t.ID).HasColumnName("id");
this.Property(t => t.Name).HasColumnName("name");
this.Property(t => t.StartScriptID).HasColumnName("startscript_id");
this.HasOptional(t => t.StartScript)
.WithMany()
.HasForeignKey(t => t.StartScriptID);
}
}
public class ScriptMap : EntityTypeConfiguration<Script>
{
public ScriptMap()
{
this.HasKey(t => t.ID);
this.ToTable("TB_APLICATION", "aplication");
this.Property(t => t.ID).HasColumnName("id");
this.Property(t => t.Name).HasColumnName("name");
this.HasRequired(t => t.Application)
.WithMany(w => w.Scripts)
.HasForeignKey(t => t.ApplicationId);
}
}
Don't forget to tell EF about the Mappings: (Call it inside OnModelCreating)
public static class MappingConfig
{
public static void ConfigureMap(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ApplicationMap());
modelBuilder.Configurations.Add(new ScriptMap());
}
}
I am using Entity Framework 6.0 with code first.
I want to create this table
This is the User model.
public partial class User : IUser
{
public virtual string Firstname { get; set; }
public virtual string Lastname { get; set; }
public virtual User ManagerUser { get; set; }
[ForeignKey("ManagerUser")]
public virtual Nullable<int> ManagerUserID { get; set; }
[Key]
public virtual int UserID { get; set; }
}
This is what I get when the table is created
Why the result is different from what I am trying to achieve?
What should I have to change to get the table as I want it?
I have resolved using this code
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<EFUser>()
.HasOptional(c => c.ManagerUser)
.WithMany()
.HasForeignKey(c => c.ManagerUserID);
}
It would be great to have solution using Attributes, meanwhile I'll use this technique