Entity Framework 4.1 Code first mapping to tables that have their primary key as the foreign key column - entity-framework-4.1

I have an existing database that I'm using Entity Framework Code First to map. The naming convention for columns is odd, so I decided I'd map entity properties manually, and up until now this has been fine.
The schema for the database is fairly strange to me and is definitely not how I would've done it. Unfortunately, I'm stuck with it for the time being.
Basically there is a single primary key (AccountNumber) shared by a number of tables creating a bunch of one-to-one relationships. However, the primary key is also the foreign key column. Here is what my entities look like (with a whole bunch of properties removed for simplicity). I've only included two entities to make it easy.:
public class Customer
{
public int AccountNumber { get; set; }
public String PhoneNumber { get; set; }
...
public virtual Address Address { get; set; }
}
public class Address
{
public int AccountNumber { get; set; }
public String Name { get; set; }
public String Address1 { get; set; }
public String City { get; set; }
...
public virtual Customer Customer { get; set; }
}
The two entities share the same primary key. I've created configuration classes to do the mapping like this:
public class CustomerConfiguration : EntityTypeConfiguration<Customer>
{
public CustomerConfiguration()
: base()
{
HasKey(p => p.AccountNumber);
Property(p => p.AccountNumber).
HasColumnName("cm_l_acct").
IsRequired();
Property(p => p.PhoneNumber).
HasColumnName("cm_s_phonenumber");
HasRequired(x => x.Address).
WithRequiredPrincipal(x => x.Customer).
Map(x => x.MapKey("am_l_acct"));
}
}
public class AddressConfiguration : EntityTypeConfiguration<Address>
{
public AddressConfiguration()
: base()
{
HasKey(p => p.AccountNumber);
Property(p => p.AccountNumber).
HasColumnName("am_l_acct").
IsRequired();
...
}
}
The foreign key mapping is only done on one side. This appears like it would work if not for the fact that the foreign key column is also the primary key of the table. When I try to run a query, I get the error:
(256,6): error 0019: Each property name in a type must be unique. Property name 'am_l_acct' was already defined.
Unfortunately, I can't pull the mapping for the AccountNumber property off of the Address entity because it is the primary key.
Is there a way I can accomplish this mapping, or is it impossible?

Removet this Map(x => x.MapKey("am_l_acct")) from your Customer mapping. This mapping is only used if you map want to define FK column in database and you don't have FK property in the class but you have it - it is primary key in the Address entity. If you try to map FK that way EF thinks that you are trying to create to columns with the same name.

Related

How to create a dependency graph using composite keys in EF Core

Trying to store a composite key table which is keyed for both fields to the table it defines dependencies for.
Example case
Import files: 1..10
Dependencies 1: 2,3; 2: 4,5; 4:10
Intent is to use this key-only table for code to do code first strongly typed definitions while also being light weight, and it seemed like the most straight forward way to do it before running into problems.
Current code:
public class ImportFileDependency
{
[Key]
[ForeignKey("ImportFile")]
public int ImportFileId { get; set; }
[ForeignKey("Id")]
public ImportFile ImportFile {get; set;}
[Key]
[ForeignKey("ImportFile")]
public int ImportFileDependencyId { get; set; }
[ForeignKey("Id")]
public ICollection<ImportFile> ImportFileDependencies { get; set; }
}
public class ImportFile
{
[Key]
public int Id {get; set;}
public string Name { get; set; }
public string WorkbookTab { get; set; }
public string File { get; set; }
public ICollection<ImportFileDependency> Dependencies { get; set; }
}
...
modelBuilder
.Entity<ImportFileDependency>(e =>{
e.HasKey(ifd => new { ifd.ImportFileId, ifd.ImportFileDependencyId });
e.HasOne(ifd => ifd.ImportFile)
.WithMany(i => i.Dependencies);
});
modelBuilder
.Entity<ImportFile>()
.HasMany(i => i.Dependencies)
.WithOne()
.HasForeignKey(z => z.ImportFileId);
...
After multiple revisions of following the responses of the add-migration exception response, currently on:
There are multiple properties pointing to navigation 'ImportFile' in entity type 'ImportFileDependency'. To define composite foreign key using data annotations, use ForeignKeyAttribute on navigation.
which did not update from the most recent iteration.
I seem to have recursed into a deadend so looking for guidance
Given the time you've asked it, you probably found the answer yourself or gave up on it, but if someone else struggles with this error, this solved my issue: Entity Framework Code First - two Foreign Keys from same table
You have to define the relationship using fluent API.

Entity Framework Code First: FOREIGN KEY constraint may cause cycles or multiple cascade paths

Entity Framework Code First can generate the DB for the following POCOs.
public class Item {
public int Id { get; set; }
public string Name { get; set; }
}
public class ItemPair {
public int Id { get; set; }
public virtual Item FirstItem { get; set; }
public virtual Item SecondItem { get; set; }
}
I would like to establish the relationship with First and Second item via ID fields rather than the an entire "Item" class. So:
public class ItemPair {
public int Id { get; set; }
public virtual Item FirstItem { get; set; }
public int FirstItem_Id { get; set; }
public virtual Item SecondItem { get; set; }
public int SecondItem_Id { get; set; }
}
also works. Edit: This didn't actually work. Just generates additional FirstItem_Id1 and SecontItem_Id2 columns.
But just changing the foreign key properties to FirstItemId, SecondItemId, (without the underscore) like so:
public class ItemPair {
public int Id { get; set; }
public virtual Item FirstItem { get; set; }
public int FirstItemId { get; set; }
public virtual Item SecondItem { get; set; }
public int SecondItemId { get; set; }
}
results in the following exception.
{"Introducing FOREIGN KEY constraint 'ItemPair_SecondItem' on table 'ItemPair' may cause
cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION,
or modify other FOREIGN KEY constraints.\r\nCould not create constraint.
See previous errors."}
Why? And what can I do to avoid this exception.
I decided to just remove the cascade delete convention.
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
}
The reasons are:
I prefer to mark records deleted or deactivated for audit purposes.
At most I delete just the junction / mapping tables.
With an ORM It is relatively trivial to loop through and delete child records in the rare case I need to.
Thank you Ladislav Mrnka pointing me in the right direction.
My expectation is that in the first case your Id properties are not used in the database as FKs and EF will create another two columns (you can validate this by forcing pairing of navigation property with FK property using ForeignKeyAttribute). In the second case EF will correctly recognize your properties but it will also use cascade delete convention which will cause error in SQL server. You have two properties from the table pointing to the same parent. Actually in the database you can createItemPair from the same Item (both FKs set to the same Id). If both relations have cascade delete enabled it will result in multiple cascade paths => not allowed in SQL server.
The solution here is fluent mapping to manually define how the relations are mapped. Here is the example.

entity framework fluent api - create forgein key based on naming convention

I start learn EF Fluent API.
I have 2 simple POCO classes.
public class Customer
{
public int CustomerId{ get; set;}
public string Name{ get; set;}
}
public class Project
{
public int ProjectId { get; set; }
public int CustomerId { get; set; }
public string Name { get; set; }
public Customer Customer { get; set; }
}
context class
public class MyCtx:DbContext
{
public DbSet<Project> Projects { get; set; }
public DbSet<Customer> Authors { get; set; }
public MyCtx(string connString):base(connString)
{}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//PK
modelBuilder.Entity<Project>()
.HasKey(p => p.ProjectId)
.Property(p => p.ProjectId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
.HasColumnName("PROJECT_ID")
.IsRequired();
modelBuilder.Entity<Project>()
.Property(c => c.Name)
.HasColumnName("NAME")
.IsRequired();
//--------------------------------------------------------------------
//PK
modelBuilder.Entity<Customer>()
.HasKey(c => c.CustomerId)
.Property(c => c.CustomerId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
.HasColumnName("CUSTOMER_ID")
.IsRequired();
modelBuilder.Entity<Customer>()
.Property(c => c.Name)
.HasColumnName("NAME")
.IsRequired();
base.OnModelCreating(modelBuilder);
}
}
I defined that CustomerId will be primary key in table customer and ProjectId will be primary key in project table.
I am little surprised with this behavarior. CustomerId in project table is automatically foreign key.
This behavior is based on naming convention? Or how it works?
Yes, it is based on a naming convention, in this case specifically the NavigationPropertyNameForeignKeyDiscoveryConvention:
Convention to discover foreign key properties whose names are a
combination of the dependent navigation property name (Customer in your case) and the
principal type primary key property name(s) (CustomerId in your case).
Or it is the PrimaryKeyNameForeignKeyDiscoveryConvention:
Convention to discover foreign key properties whose names match the
principal type primary key property name(s).
I am not sure which one.
If you don't like one of those conventions you can remove them with the model builder:
modelBuilder.Conventions
.Remove<NavigationPropertyNameForeignKeyDiscoveryConvention>();
// etc.
A complete list of all conventions is here.

Entity Framework 4.1 Code First, One-to-One with one table joining to a single key field of of composite key

I'm just beginning with EF4.1 Code First, and I pretty like it.
Here's the story :
A Codif class is composed of a key from a Domaine class, an Entite class, and Reference class, and a fourth field which is some text.
Reference and Codif have an one-to-one relationship.
Thing is, when it creates the Database, it creates some ugly fields in my Reference entity, creating duplicate fields of the Codif Entity.
Good point : When I manipulate my Reference object however, I have the expected behaviour of accessing the Codif property, and the duplicate fields are invisible.
Here's the code :
public class Reference
{
public int ReferenceId { get; set; }
public string Libelle { get; set; }
public virtual Codif Codif { get; set; }
}
public class Domaine
{
public int DomaineId { get; set; }
public string Libelle { get; set; }
}
public class Codif
{
[Key, Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int DomaineId { get; set; }
[InverseProperty("DomaineId")]
public virtual Domaine Domaine { get; set; }
[Key, Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int EntiteId { get; set; }
[InverseProperty("EntiteId")]
public virtual Entite Entite { get; set; }
[Key, Column(Order = 2)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ReferenceId { get; set; }
[InverseProperty("ReferenceId")]
public virtual Reference Reference { get; set; }
[Key, Column(Order = 3)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Codification { get; set; }
}
public class Entite
{
public int EntiteId { get; set; }
public string Nom { get; set; }
public string Email { get; set; }
}
And here's the result in the tables (images) :
Codif
Reference
In the Reference class, how can I specify that ReferenceId is the foreign key to be used against a SINGLE field of Codif ?
How to get rid of those duplicate fields in Reference ?
How to remove the Reference_ReferenceId in Codif table while preserving the navigation property ?
Thank you for your support.
Marc
Edit : I'm working with an SQL Compact Edition 4 database
Replace all your [InverseProperty("xxx")] attributes by [ForeignKey("xxx")]. I think that this is what you actually want. [InverseProperty] refers to the navigation property on the other side of the relationship which can never be a scalar property.
Edit
You could in addition to the FK attribute set the [InverseProperty] attribute on the Reference property in your Codif class:
public class Codif
{
//...
[Key, Column(Order = 2)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ReferenceId { get; set; }
[ForeignKey("ReferenceId")]
[InverseProperty("Codif")] // <-- refers to Codif property in Reference class
public virtual Reference Reference { get; set; }
//...
}
But it think it's not really necessary because EF should detect the correct relationship between Reference and Codif by convention. (I'm not sure though for one-to-one relationships.)
Edit
Problems!
First: As far as I can see you must specify the one-to-one relationship in Fluent API because EF cannot determine otherwise what's the principal and what's the dependent:
modelBuilder.Entity<Reference>()
.HasOptional(c => c.Codif)
.WithRequired(c => c.Reference);
Second: EF will still complain because of the composed key or because ReferenceId is not the key alone. If ReferenceId were the only key in Codif it would work.
Edit
I'm trying to understand what you want to achieve. Apparently your composed key in Codif is supposed to ensure that any combination of the four field values can exist only once. But this conflicts with the one-to-one relationship imo, for example this would be valid table entries:
Table Codif:
DomaineId EntiteId ReferenceId Codification
----------------------------------------------------
1 1 1 "A"
2 1 1 "A"
1 2 1 "A"
1 1 2 "A"
1 1 1 "B"
etc...
But as you can see: You can have multiple rows with the same ReferenceId which means that you cannot have a one-to-one relationship to Reference. Here you have 4 Codif entities which refer to the same Reference entity with Id = 1.
Now, I guess, the fact that you want to have a one-to-one relationship means that there is an additional constraint so that ReferenceId in Codif table can occur only once. In other words: The rows 2, 3 and 5 in the example above are invalid from business viewpoint (although valid from DB viewpoint).
If this is the case I would actually make ReferenceId the single key in Codif and make sure from business logic that the other combinations of values in the DB are unique (Query if exists before you insert a new Codif). On database side you could create a unique index over the other three fields to ensure that the combinations are always unique in the database. EF cannot check this internally though since unique constraints are not yet supported.

One-To-Many relationship with UniqueKey constraint

I'm new to Fluent-NHibernate.
My problem is that a want to establish a one-to-many relationship between two entities.
A product can have multiple (unique) revisions, and a specific revision only belongs to one product.
So for example Product1 has a revision "a" and a revision "b", but can't have two revisions "a". That's what my class definitions look like:
public class Product
{
public virtual int ID {get; private set;}
public IList<ProductRevision> revisions { get; set; }
}
public class ProductRevision
{
public virtual int ID {get; private set;}
public Product isRevisionOf { get; set; }
public virtual string revision { get; set; }
}
Here's my mapping
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(x => x.ID).Column("ProductNo");
HasMany(x => x.revisions).Cascade.All();
}
}
public class ProductRevisionMap : ClassMap<ProductRevision>
{
public ProductDefinitionFormationMap()
{
Id(x => x.ID);
References(x => x.isRevisionOf).UniqueKey("Product_Revision").Not.Nullable();
Map(x => x.revision).UniqueKey("Product_Revision").Not.Nullable();
}
}
What I get now is redundant column "Product_Id" in the ProductRevision table. A'm I getting something wrong by defining the relation on BOTH sides with hasMany() and references().
Normally I wouldn't define the References(...) mapping, but I need it for the uniquekey constraint, don't I?
Thanks,
Erik
This is bi-directional one-to-many relation and the mapping may look something like this:
public ProductMap()
{
Id(x => x.ID).Column("ProductNo");
HasMany(x => x.revisions)
.KeyColumn("ProductId") // the name of the FK column in ProductRevision table
.Inverse() // bi-directional relation
.Cascade.All();
}
public ProductRevisionMap()
{
Id(x => x.ID);
References(x => x.isRevisionOf)
.Column("ProductId") // column name must match the name specified in Product HasMany
.UniqueKey("Product_Revision")
.Not.Nullable();
Map(x => x.revision).UniqueKey("Product_Revision").Not.Nullable();
}
Inverse and KeyColumn are the missing pieces of this puzzle.
This mapping generates required constraint in database but you will still need to check this constraint in business logic as well otherwise you will get SqlException if a revision with duplicate name is inserted into revisions collection.