I have a foreign key field that is generated by Entity Framework (6) at runtime. This field is related to a interfaces T field. So we end up with a field called InterfaceFieldName_Id - I would like to rename this (prefereably via the Fluent API) to TypeFieldName_Id. I know how I can do this by adding in FK Id field to the POCO class, but I would prefer not to do that.
Is there a way to rename this generated field name?
You can use Map method to rename a Foreign Key that is not defined in the Model:
modelBuilder.Entity<Entity1>()
.HasRequired(c => c.Entity2NavProperty)
.WithMany(t => t.Entity1NavigationProperty)
.Map(m => m.MapKey("TypeFieldName_Id"));
If you already have a property in your model that represent the FK and it was renamed by EF in your DB, then you should check your relationship.Code First falls back to automatically introducing one in the database in case the foreign key property hasn't been discovered by convention, and none is configured. The foreign key property will be discovered by convention if it is named [Target Type Key Name], [Target Type Name] + [Target Type Key Name], or [Navigation Property Name] + [Target Type Key Name].
Now to configure explicitly what is the FK property you want to use, you can can do the following:
modelBuilder.Entity<Entity1>()
.HasRequired(c => c.Entity2NavProperty)
.WithMany(t => t.Entity1NavigationProperty)
.HasForeignKey(d => d.TypeFieldName);
And if you want to use a different name for TypeFieldName in your DB, then you can also do this:
modelBuilder.Entity<Entity1>().Property(d=>d.TypeFieldName).HasColumnName("TypeFieldName_Id")
You can specify the column name fluently:
modelBuilder.Entity<YourEntity>()
.HasRequired(c => c.InterfaceFieldName)
.WithMany(d => d.YourColumnName)
.HasForeignKey(c => c.FieldName);
Related
I found related question but my issue seems to be different.
Running the following code:
var dbitem = context.MyDatabaseItems.Single(p => p.Id == someId);
context.Update(dbitem);
context.SaveChanges();
Results in "Cannot update identity column 'Id'". Table behind is a bit special. "Id" is NOT the primary key for different reasons. Primary key consists of combination of other fields. No matter what I do: detaching, reattaching etc etc the existing item I am unable to save the entity even if I do not change it (see the code).
However this Id is unique and auto generated.
The builder is the following:
builder.Property(p => p.Id)
.ValueGeneratedOnAdd();
builder.HasKey(p => new { p.BusinessDay, p.ClientId, p.Version });
BusinessDay is dateTime, CLientId and Version are integers.
What is going on here?
There are two metadata properties which control the update behavior called BeforeSaveBehavior and AfterSaveBehavior.
For auto generated keys the later is assumed to be Ignore, i.e. never update. For non key auto generated properties it must be configured explicitly (note that there is no fluent API for that so far, so you have to use the metadata API directly), e.g.
// First define the new key
builder.HasKey(p => new { p.BusinessDay, p.ClientId, p.Version });
// Then configure the auto generated column
// This (especially the `SetAfterUpdateBehavior` call) must be after
// unassociating the property as a PK, otherwise you'll get an exception
builder.Property(p => p.Id)
.ValueGeneratedOnAdd()
.Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); // <--
This does not change the database schema (model), hence no migration is needed. Just the EF Core update entity behavior.
I have an existing database with a table to which I have added 2 new columns.
Example table:
CREATE TABLE [dbo].[Tasks](
[TaskId] [int] IDENTITY(1,1) NOT NULL,
[CategoryId] [int] NOT NULL,
[ResolutionId] [int] NULL,
[AmendedCategoryId] [int] NULL, -- New column
[AmendedResolutionId] [int] NULL, -- New column
)
These columns (each is an int) have a foreign key relationship which is the same as the 2 previously existing columns ("CategoryId" and "AmendedCategoryId" each FK to [TaskCategory] and "ResolutionId" and "AmendedResolutionId" each FK to [TaskResolution]).
When I use EF6 Code First to generate the C# data access classes for this, the results are confusing (to say the least).
Here is an example of what EF is producing:
modelBuilder.Entity<TaskCategory>()
.HasMany(e => e.TaskCategory1)
.WithOptional(e => e.TaskCategory2)
.HasForeignKey(e => e.ParentCategoryId);
modelBuilder.Entity<TaskCategory>()
.HasMany(e => e.Tasks)
.WithOptional(e => e.TaskCategory)
.HasForeignKey(e => e.AmendedCategoryId);
modelBuilder.Entity<TaskCategory>()
.HasMany(e => e.Tasks1)
.WithRequired(e => e.TaskCategory1)
.HasForeignKey(e => e.CategoryId)
.WillCascadeOnDelete(false);
Resulting in the following:
The class property names for the new "Amended" columns (ex: "AmendedTaskCategory", "AmendedTaskResolution") do not match the
column names from the table, although the "AmendedTaskCategoryId"
and "AmendedTaskResolutionId" names DO match. Instead they have
"TaskCategory1" and "TaskResolution1" for their names.
The "modelBuilder.Entity" code that was generated is mapping the
"original" column to the new equivalent (ex: "TaskCategory" is being
mapped to "AmendedTaskCategory"), and the original generated
property is being left null (which is causing problems down-stream).
Why is EF generating the "1" and "2" classes &
properties?
How can I get Entity Framework 6 to "correctly" name and map the generated the classes and properties in the classes resulting from using the "Code First" EF functionality?
If you all need more details I will send them for sure. Here is a screen shot containing 2 tables:
Basically this is what looks like in SQL today:
I am trying to use the fluent API for the relationship but not sure how to do it.
Table 1 has a PK and a FK.
Table 2 does not have a PK, only FK.
Below is an example of what I need but this code applies to a different set of tables. I am trying to get the "relationship" syntax correct for the scenario described here:
this.ToTable("Server");
//primary key
this.HasKey(t => t.serverId);
//properties
...
//relationships
this.HasMany(n => n.NetworkAdapters)
.WithRequired(s => s.Server)
.HasForeignKey(s => s.serverId)
.WillCascadeOnDelete(true);
Thank you
Without the primary key on table 2 you are most likely looking for a One-to–Zero-or-One relationship. You might possibly be requiring both ends which I will describe too.
One-to–Zero-or-One
// Configure the primary key for Table1
modelBuilder.Entity<Table1>()
.HasKey(t => t.networkAdapterId);
// Map one-to-zero or one relationship
modelBuilder.Entity<Table2>()
.HasRequired(t => t.Table1)
.WithOptional(t => t.Table2);
One-to-One
// Configure the primary key for the Table1
modelBuilder.Entity<Table1>()
.HasKey(t => t.networkAdapterId);
// Map one-to-one relationship
modelBuilder.Entity<Table2>()
.HasRequired(t => t.Table1)
.WithRequiredPrincipal(t => t.Table2);
See here for more details
I have 2 entities,
News
FileAttachment
I wanted to configure using code-first fluent API so that Each News can have 0,1 or more than 1 attachments.
here is what i'm using right now
public NewsMap()
{
this.ToTable("News"); // Table Name
this.HasKey(m => m.Id); // Primary Key
// Field Definition
this.Property(m => m.Title).HasMaxLength(255).IsRequired();
this.Property(m => m.Body).HasColumnType("Text").IsRequired();
this.Property(m => m.Summary).HasMaxLength(1000).IsRequired();
this.Property(m => m.AuthorId).IsRequired();
this.Property(m => m.CreatedOn).IsRequired();
this.Property(m => m.UpdatedOn).IsRequired();
this.HasMany(m => m.Attachments).WithMany().Map(m => m.MapLeftKey("NewsId").MapRightKey("AttachmentId"));
}
public class FileAttachmentMap : EntityTypeConfiguration<FileAttachment>
{
public FileAttachmentMap()
{
this.ToTable("FileAttachments"); // Table Name
this.HasKey(m => m.Id); // Primary Key
// Field Definition
this.Property(m => m.DisplayName).HasMaxLength(256).IsRequired();
this.Property(m => m.PhysicalFileName).HasMaxLength(256).IsRequired();
this.Property(m => m.Extension).HasMaxLength(50).IsRequired();
this.Property(m => m.IsImage).IsRequired();
this.Property(m => m.ThumbTiny).HasMaxLength(275).IsOptional();
this.Property(m => m.ThumbSmall).HasMaxLength(275).IsOptional();
this.Property(m => m.ThumbMid).HasMaxLength(275).IsOptional();
this.Property(m => m.ByteSize).IsRequired();
this.Property(m => m.StorageType).IsRequired();
this.Property(m => m.CreatedOn).IsRequired();
this.Property(m => m.UpdatedOn).IsRequired();
}
}
This mapping correctly generates an intermediate table named NewsFileAttachment with two fields :
NewsId
AttachmentId
On News Entity when i call News.Attachments.Add(Attachment); it correctly adds records in both Attachment & NewsAttachment tables.
When i remove some list item from News.Attachments it correctly removes record from NewsAttachment table, but it doesn't delete record in FileAttachment table. I wanted to remove that too.
Can someone please suggest a better Fluent API configuration to achieve this?
Thanks,
Amit
EDIT
In my case FileAttachment stores files for various purpose. i've Blog entity that too have attachments. So, two intermediate tables BlogAttachments & FileAttachments. Now if i use WithOptional as (I can't use WithRequired as i need BlogId & NewsId both in FileAttachment table), i can get rid off intermediate table, but still delete doesn't delete record from FileAttachment table, it just make NewsId/BlogId NULL.
Any suggestion? Main thing is I do not wanted to create separate tables with all the fields i have in FileAttachment table.
That's expected - as it creates many-to-many and extra table - the cascade only applies to that table.
There is no direct 'FK' relationship in between your News and
Attachment, as it goes through a join table. And thus you cannot expect for e.g. attachment to be deleted, if the news does - as attachment could have other news relating to it.
See also this one - it's somewhat relevant.
One to Many Relationship with Join Table using EF Code First
i.e. if your structure permits don't explicitly create many-to-many (don't put collection on both sides, or similar in fluent config).
In your case providing your 'attachments' are not reusable in between News - then just put a collection navigation property in the News - and leave attachment w/o any - or make a 'FK', single instance navigation from Attachment (like a 'Parent') if you need it.
On the other side, if an attach... could be parented by different
news records - then you shouldn't have cascade delete anyways.
note: check your generated migration script - or SQL/Db - to see exactly what it creates - and make sure there is no intermediate table created - and only one 'FK' going from 'attachment' to 'news'.
edit:
modelBuilder.Entity<News>()
.HasMany(c => c.Attachments)
.WithOptional() // or WithRequired (test to see which is better for you)
.WillCascadeOnDelete(true);
...and make one public ICollection<FileAttachment> Attachments {get;set;} in the News.
(actually the collection property is all you need - but configuration is to be safe you get what you want)
That'd make you 1-to-many (or many-to-one), which is the nature of your data (as you said in comments) - and you can have cascade deletes.
When I create two entities with many to many relationship, it will generate a relationship table in the database, is it possible to specify the table's name?
Yes but you have to use fluent API:
mb.Entity<FirstEntity>()
.HasMany(a => a.SecondEntities)
.WithMany(b => b.FirstEntities)
.Map(mc =>
{
mc.ToTable("YourTableName", "YourDbSchema");
mc.MapLeftKey("FirstEntityKeyColumnName");
mc.MapRightKey("SecondEntityKeyColumnName");
});