Why fluent nhibernate automapping creates many foreign keys instead of one? - c#

I use Fluent Nhibernate and have 2 entities:
public class Document
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual User Author { get; set; }
public virtual DateTime Date { get; set; }
}
and
public class User
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual IList<Document> Docs { get; set; }
public User()
{
Docs = new List<Document>();
}
}
and I don't understand why fnh creates that wrong schema on this simpliest entities. That's what fnh creates in my db:
I can't understand why fnh creates 2 references (Author_id and User_id) to User table instead of a single reference (only Author_id).
I found an workaround here Fluent Nhibernate AutoMapping -- 2 foreign keys to same table? and here Fluent NHibernate Automappings generating 2 foreign keys for 1 relationship but I don't want to use it because I don't understand why I should set up every thing by my hands if I use automappings that should do all work for me (at-least that simpliest and obvious mappings as in my entities).

You have a Document entity referring a User entity (0-1 relationship) through a property named Author, but in the same time, in User entity you refer Document in a one-to-many relationship.
Fluent NHibernate automapping works with conventions, and the specific HasManyConvention maps the relationship creating a foreign key name based on the NAME (and not the type) of the referring entity (in this case USER)
So NHibernate, when creating the relationship between User and Document, creates a User_Id key in the Document table. This is a correct convention behavior.

Related

EF 6 code-first generating extra table for many-many table?

Referencing from the #Ogglas answer of this post,
I would like to ask if it is normal for EF to generate another table?
If the additional table should not be there, then what am I doing wrong here? Please enlighten me. TIA!
Sample code:
public class Aggregate
{
public Aggregate()
{
Episodes = new HashSet<Episode>();
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
...
public virtual ICollection<Episode> Episodes { get; set; }
}
public class Episode
{
public Episode()
{
Aggregates = new HashSet<Aggregate>();
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
...
public virtual ICollection<Aggregate> Aggregates { get; set; }
}
public class EpisodeAggregate
{
[Key]
[Column(Order = 1)]
[ForeignKey("Episode")]
public Guid EpisodeId { get; set; }
[Key]
[Column(Order = 2)]
[ForeignKey("Aggregate")]
public Guid AggregateId { get; set; }
public virtual Episode Episode { get; set; }
public virtual Aggregate Aggregate { get; set; }
public DateTime Timestamp { get; set; }
}
In my DbContext.cs:
public DbSet<EpisodeAggregate> EpisodeAggregates { get; set; }
You are right. In a relational database, a many-to-many relation is solved using a junction table.
For a standard many-to-many relationship, you don't need to mention this junction table; entity framework recognizes the many-to-many by your use of the virtual ICollection<...> on both sides of the many-to-many relation, and will automatically create the tables for you.
To test my database theories and entity framework, I quite often use a simple database with Schools, Students and Teachers. One-to-many for School-Student and School-Teacher and many-to-many for Teacher-Student. I always see that the Teacher-Student junction table is created automatically, without ever having to mention it.
However!
Your junction table is not standard. A standard junction table has only two columns: the EpisodeId and the AggregateId. It doesn't even have an extra primary key. The combination [EpisodeId, AggregateId] is already unique and can be used as a primary key.
You have in table EpisodeAggregate an extra column: TimeStamp. Apparently you want to know when an Episode and an Aggregate got related.
"Give me the TimeStamp when Episode[4] got related with Aggregate[7]"
This makes that this table is not a standard junction table. There is no many-to-many relation between Episodes and Aggregates. You made a one-to-many relation between Episode and its Relations with the Aggregates, and similarly a one-to-many relation between Aggregates and its Relations with the Episodes.
This makes that you have to change your many-to-many into one-to-many:
class Episode
{
public Guid Id { get; set; }
// every Episode has zero or more Relations with Aggregates (one-to-many)
public virtual ICollection<EpisodeAggregateRelation> EpisodeAggregateRelations { get; set; }
...
}
class Aggregate
{
public Guid Id { get; set; }
// every Episode has zero or more Relations with Episodes(one-to-many)
public virtual ICollection<EpisodeAggregateRelation> EpisodeAggregateRelations { get; set; }
...
}
class EpisodeAggregateRelation
{
// Every Relation is the Relation between one Episode and one Aggregate
// using foreign key:
public Guid EpisodeId { get; set; }
public Guid AggregateId { get; set; }
public virtual Episode Episode { get; set; }
public virtual Aggregate Aggregate { get; set; }
public DateTime Timestamp { get; set; }
}
If you are certain the there will always be at utmost one relation between an Episode and an Aggregate, you can use the combination [EpisodeId, AggregateId] as a primary key. If you think these two might have several relations, you need to add a separate primary key.
I often use my classes in different databases, hence I don't like attributes, I solve it in fluent API in OnModelCreating:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Episode>()
.HasKey(episode => episode.Id)
// define the one-to-many with EpisodeAggregateRelations:
.HasMany(episode => episode.EpisodeAggregateRelations)
.WithRequired(relation => relation.Episode)
.HasForeignKey(relation => relation.EpisodeId);
modelBuilder.Entity<Aggregate>()
.HasKey(aggregate => aggregate.Id)
// define the one-to-many with EpisodeAggregateRelations:
.HasMany(aggregate => aggregate .EpisodeAggregateRelations)
.WithRequired(relation => relation.Aggregate)
.HasForeignKey(relation => relation.aggregateId);
The above is not needed!
Because you followed the entity framework code first conventions, you can omit these two statements. Entity framework will recognize the primary key and the one-to-many relation. Only if you want to deviate from the conventions, like a non-standard table name, or if you want to define the column order:
modelBuilder.Entity<Episode>()
.ToTable("MySpecialTableName")
.Property(episode => episode.Date)
.HasColumnName("FirstBroadcastDate")
.HasColumnOrder(3)
.HasColumnType("datetime2");
But again: you followed the conventions, all those attributes like Key, ForeignKey, DatabaseGenerated are not needed. And the column order: who cares? Let your database management system decide about the most optimum column order.
My advice would be: try to experiment: leave out this fluent API and check whether your unit tests still pass. Checked in five minutes.
The EpisodeAggregateRelation has something non-standard: it has a composite primary key. Hence you need to define this. See Configuring a composite primary key
modelBuilder.Entity<EpisodeAggregateRelation>()
.HasKey(relation => new
{
relation.EpisodId,
relation.AggregateId
});
If you already defined the one-to-many in Episodes and Aggregates, or if that was not needed because of the conventions, you don't have to mention this relation here again.
If you want, you can put the one-to-many in the fluent API part of EpisodeAggregateRelation, instead of in the fluent API part of Episode / Aggregate:
// every EpisodeAggregateRelation has one Episode, using foreign key
modelBuilder.Entity<EpisodeAggregateRelation>()
.HasRequired(relation => relation.Episode(
.WithMany(episode => episode.EpisodeAggregateRelations)
.HasForeignKey(relation => relation.EpisodeId);
// similar for Aggregate
One final tip
Don't create a HashSet in the constructor. It is a waste of processing power if you fetch data: you create the HashSet, and it is immediately replaced by the ICollection<...> that entity framework creates.
If you don't believe me: just try it out, and see that your unit tests pass, with the possible exception of the unit test that checks for an existing ICollection<...>

Entity Framework - Explicitly mapped ID columns

Consider the relationship between the following entities:
class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public int AuthorId { get; set; }
public Author Author { get; set; }
}
class Author
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
}
Okay, nobody in their right minds would have a one-to-one relationship in this context; that's not the issue at play here...
You'll notice that for each navigation property (Author and Post) there are explicit Id columns defined (AuthorId and PostId) respectively.
Personally I don't like this approach (though I can see some potential benefit). I'd prefer EF to manage the Id columns internally for me, and just let me expose a relationship between Post and Author.
What I want to know is, is there any official recommendation for or against explicit Id columns?
NOTE: I do know of one place where explicit Id mapping is valuable, and that is when you're implementing a many-to-many join table. You can use the Ids to create a unique constraint which prevents record duplication for the same many-to-many relationship.
What I want to know is, is there any official recommendation for or against explicit Id columns?
Yes:
It is recommended to include properties in the model that map to
foreign keys in the database. With foreign key properties included,
you can create or change a relationship by modifying the foreign key
value on a dependent object. This kind of association is called a
foreign key association. Using foreign keys is even more essential
when working with N-Tier applications.
Entity Framework Relationships and Navigation Properties
This is not an "official recommendation". But here is how I see it.
You want the navigational properties to be virtual for lazy loading, and you will need the two Id columns for the mapping into the database.
But your code never uses those foreign keys. Here is an example that links a Post with an Author.
var p = new Post {Title="Foo"};
p.Author = _db.Authors.First(a => a.Id == 5);
_db.Posts.Add(p);
_db.SaveChanges();
You also need to map those fields up into your domain layer to keep track of relations.

Entity Framework foreign key to multiple tables/entities

I need to implement Entity-Attribute-Value functionality on multiple data tables using Entity Framework. Let's say I have an attribute value EF class that looks like this:
public class EntityAttributeValue
{
// Not important to my question.
public virtual Entity ParentEntity { get; set; }
public virtual EntityAttribute ParentEntityAttribute { get; set; }
// Field in question.
public Guid ParentSurrogateKey { get; set; }
public string Value { get; set; }
...
}
Then I have multiple entities that have supplementary EAV values associated with them:
public class Entity1
{
// Key. EntityAttributeBalue.ParentSurrogateKey maps to this.
[Key]
public Guid SurrogateKey { get; set; }
// Standard properties.
public string Property1 { get; set; }
public string Property2 { get; set; }
// Collection of EAV values associated with this entity/table.
[ForeignKey("ParentSurrogateKey")]
public virtual IList<EntityAttributeValue> EntityAttributeValues { get; set; }
}
public class Entity2
{
// Key. EntityAttributeBalue.ParentSurrogateKey maps to this.
[Key]
public Guid SurrogateKey { get; set; }
// Standard properties.
public string OtherProperty1 { get; set; }
public string OtherProperty2 { get; set; }
// Collection of EAV values associated with this entity/table.
[ForeignKey("ParentSurrogateKey")]
public virtual IList<EntityAttributeValue> EntityAttributeValues { get; set; }
}
My problem is that both Entity1 and Entity2 have EntityAttributeValue objects associated with them. Code first migrations tries to create a foreign key from EntityAttributeValue back to Entity1 and another one back to Entity2 on ParentSurrogateKey. The surrogate key for any single given EntityAttributeValue is only associated with either one Entity1 or one Entity2 (or, expanding out, one EntityN...), not both/all.
I have a many to many relationship here, but one side not only maps to multiple rows, but multiple entities/tables over a shared GUID column.
How should I be approaching this? Should I just remove the EntityAttributeValue foreign keys back to Entity1 and Entity2 from the automatic migration (which would be a long term pain)? Should I be manually retrieving the list of EntityAttributeValues for a given EAV entity instead of relying on EF to do it for me?
Well, the answer turned out to be obvious and simple. I needed to define a many-to-many relationship with FluentAPI. In OnModelCreating, I just added:
modelBuilder.Entity<Entity1>()
.HasMany(m => m.EntityAttributeValues)
.WithMany();
modelBuilder.Entity<Entity2>()
.HasMany(m => m.EntityAttributeValues)
.WithMany();
I thought I had tried this, but I guess I hadn't. Because the many-to-many relationship creates an intermediate table for each entity and the foreign keys are on that intermediate table (and there is only a row in the intermediate table when a given EntityAttributeValue applies to a given Entity), no foreign key issues.

Entity Framework one-to-one with a class that is sometimes independent

I have a parent class ComponentDesign:
public class ComponentDesign
{
public string Name { get; set; }
public virtual JobFile DesignFile { get; set; }
public int? DesignFileId { get; set; }
public Pdf PdfFile { get; set; }
public int? PdfFileId { get; set; }
public JobFile SealedPdfFile { get; set; }
public int? SealedPdfFileId { get; set; }
public int Id { get; set; }
public int JobId { get; set; }
}
And a child class JobFile (of which Pdf is a subclass):
public class JobFile
{
public int ID { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public ComponentDesign ComponentDesign { get; set; }
public int? ComponentDesignId { get; set; }
public int? JobId { get; set; }
}
These classes are stored in a relational database using Entity Framework. I would like to have navigation properties on both sides of the relationship, so that I can say componentDesign.PdfFile or jobFile.ComponentDesign at will.
Each property of type JobFile in ComponentDesign is optional, and a JobFile will only ever belong to oneComponentDesign. However, aJobFilemay be free-standing, not belonging to anyComponentDesign(sojobFile.ComponentDesign` would be null).
I'm struggling with how to use the Fluent API to configure this relationship. It is one-to-one. Is it possible to have the navigation properties on both sides of the relationship? Remember that a JobFile can be free-standing (so jobFile.ComponentDesign will not always be relevant), so there are JobFiles that belong to ComponentDesigns and ones that don't. The closest I feel I've come is this:
modelBuilder.Entity<ComponentDesign>()
.HasOptional(componentDesign => componentDesign.DesignFile)
.WithRequired(jobFile => jobFile.ComponentDesign);
But this seems to me to indicate that jobFile.ComponentDesign is always required, which isn't the case. I'm hesitant to just try it because it will generate some substantial migrations, so I wanted to get input first. What should my Fluent API configuration look like for this situation?
Just so that I understand the relationships:
ComponentDesign -> JobFile = Optional.
JobFile -> ComponentDesign = Optional.
This is a Zero-or-one to zero-or-one ([0/1]-[0/1]) relationship.
This can be implemented in the following way, using the the .WithOptionalPrincipal method in fluent API. With a 1-0 relationship, it's obvious which end is the principal end; same with a 1-∞ relationship. ∞-∞ relationships don't have a principal end, due to the hidden tables created that control the relationships between each. With a 0/1-0/1, or a 1-1 relationship, it's not obvious, and so you must tell the database which end to use as the principal end of the relationship. Among many other things, the pricipal is responsible for initiating, and maintaining the relationship between the tables.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure ComponentDesign & JobFile entity
modelBuilder.Entity<ComponentDesign>()
// Mark JobFile property optional in ComponentDesign entity.
.HasOptional(cd => cd.DesignFile)
// Mark ComponentDesign property optional in JobFile entity.
.WithOptionalPrincipal(jf => jf.ComponentDesign);
}
Or, you can create an EntityTypeConfiguration<> class for each entity to separate out the relationships, if there are a lot to sort through. While this does decentralise the relationships, it is more scalable, as these configuration classes can be injected via MEF once the context is configured to do so. Just an idea for future development.
If you were configuring the relationship from the JobFile end, you would use .WithOptionalDependent, in order to set the navigation correctly, instead of .WithOptionalPrincipal. It all depends on which side you configure the relationship from. The ComponentDesign entity relies on the JobFile entity a lot more than the JobFile entity relies on the ComponentDesignentity; thus it should be configured as the principal.
As stated in one of the comments above, complex properties should be adorned with the virtual keyword. This lazy loads the property, and also, when the class is intansiated, it will set the initial value to be null.
Clone the database to a test server, to work on this one problem, then implement the changes on the production model; that way there is no chance of data loss when migrating the database.

Using fluent API to set up composite foreign key in one-to-one relation in legacy database

I'm just a beginner in EF code first model. Given two POCO classes mapped to current legacy MS SQL database. They are associated with a composite foreign key setting up one to many relation. Since it's actually one-to-one relation I'd like to have corresponding navigation properties in my POCO objects and do mapping in fluent API. Here is my example:
public partial class Answer
{
//primary key
public int id { get; set; }
//foreign keys
public int question { get; set; }
public int assignedForm { get; set; }
//simple fields
public short state { get; set; }
public int author { get; set; }
//navigation property
public virtual AssignedQuestion AssignedQuestion { get; set; }
}
public partial class AssignedQuestion
{
// primary keys
public int id { get; set; }
public int assignedForm { get; set; }
//simple field
public string content { get; set; }
//navigation property
//public virtual ICollection<Answer> Answers { get; set; }
public virtual Answer Answer { get; set; }
}
If I wanted to do one-to-many relation I would simply uncomment "Answers" collection and have Fluent API mapping:
modelBuilder.Entity<AssignedQuestion>()
.HasKey(q => new { q.id, q.assignedForm });
modelBuilder.Entity<Answer>()
.HasRequired(a => a.AssignedQuestion)
.WithMany(aq=>aq.Answers)
.HasForeignKey(a => new { a.question,a.assignedForm});
My goal is to go with one-to-one relation and use "Answer" property in AssignedQuestion with such Fluent API as:
modelBuilder.Entity<AssignedQuestion>()
.HasKey(q => new { q.id, q.assignedForm });
modelBuilder.Entity<Answer>()
.HasRequired(a => a.AssignedQuestion)
.WithOptional(aq => aq.Answer);
//.HasForeignKey(a => new { a.question, a.assignedForm });
The problem is I can't specify exactly foreign key fields (as in previous example) and uncomment HasForeignKey call. In this case EF tries to join tables using conventional field names "AssignedQuestion_ID" and "AssignedQuestion_AssignedForm" instead of "question" and "assignedForm" in Answer table. Is there a walkaround in Fluent API other than changing field names?
It is not one-to-one relationship so your first mapping is correct. The reason why it is one-to-many is that EF understands one-to-one only when build on PKs on both sides. If AssignedQuestion has PK id and assignedForm your Answer will need to have FK and PK on its id and assignedForm otherwise EF doesn't see it as one-to-one relation. Even if you mark your question and assignedForm with unique constaint in database (to make it one-to-one in the database) EF will still not be able to handle it as one-to-one because it doesn't support unique constraints yet (except PK).

Categories