Entity Framework Code First Relationships; what am I missing? - c#

I'm experimenting with EF5 Code First and I am using the models (show below).
When I look at the database that is created, I am confused because I do not see anything in the Track table that points to the Category table. Category has a FK pointing back to Track but that means that there are going to be duplicates of the categories?
A little background: I am trying to build a model that has tracks and every track can have 1 to N Categories. All of the categories are already defined, that is they are basically a lookup and I plan to create them in the seed method when database is created.
I think I am not understanding something obvious... When I query a track, how will I know what category it contains?
Thx
public class Track : IAuditInfo
{
public Int32 Id { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public String Data { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime ModifiedOn { get; set; }
public ICollection<Category> Categories { get; set; }
public Track()
{
Categories = new List<Category>();
}
}
public class Category
{
public Int32 Id { get; set; }
public Boolean IsVisible { get; set; }
public String DisplayName { get; set; }
}

Your current model is a one-to-many relationship between tracks and categories.
This usually implemented, as you have noted that entity framework does, using a foreign key on the many side (category) to the one side (track).
If I understand you correctly, what you want is a many-to-many relationship. Many tracks can be related to the same category, and a single track can belong to many categories.
To let entity framework understand that you want a many-to-many relationship you can simply add a ICollection property to your category class.
So both your classes should have a collection of the other class.
I.e. tracks have many categories and categories have many tracks.
For more information you can also see: http://msdn.microsoft.com/en-us/data/hh134698.a.nospx

Olav is right, your data model at the moment is not telling Entity Framework that there is a many-to-many relationship in there.
The simplest way to resolve this is to add
public virtual ICollection<Track> Tracks { get; set; }
to your Category class.
However... You may not want to pollute your domain model with artefacts that are not relevant to your domain. More importantly, when you do it this way, it is up to Entity Framework to figure out what to call the binding table. Prior to EF6 this naming is non deterministic (see http://entityframework.codeplex.com/workitem/1677), which may mean that two different machines compiling the same code will decide on different names for that table and cause some interesting migration problems in your production system.
The answer to both problems is to always explicitly manage many-to-many relationships with Fluent Configuration.
In your Data Context class, override the OnModelCreating, something like this:
public class MyDb : DbContext
{
public IDbSet<Track> Tracks { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Track>()
.HasMany(t => t.Categories)
.WithMany()
.Map(c => c.ToTable("CategoriesForTracks"));
}
}
If you do this, you don't need to add a navigation property to your Category class, though you still can (if you do, you should use the overload for WithMany that allows you to specify a property).
Relationships between entities and how to map that to a relational database is inherently hard. For anything other than the simplest parent-child relationships you will want to use the fluent API to make sure you actually get what you want.
Morteza Manavi has a really good blog series describing relationships in EF Code First in exhaustive detail.
NOTE
You should usually make navigation properties virtual. So, you should change your Category class like this:
public virtual ICollection<Category> Categories { get; set; }
In theory, not making it virtual should just cause eager loading rather than lazy loading to happen. In practice I have always found lots of subtle bugs appearing when my navigation properties are not virtual.

Related

EF Core defining "complex" relation

I'm having a hard time defining the relations I want using EF Core(1 to many) .
E.G:
I am an entity of Task and Employee , each task is given by an employee and also is appointed to an employee . I've created the Task class as follow:
public class Task
{
public int TaskId { get; set; }
[ForeignKey("RequestedBy")]
[Required]
public int RequestedById { get; set; }
[Required]
[ForeignKey("TaskedTo")]
public int TaskedToId { get; set; }
public virtual Employee RequestedBy { get; set; }
public virtual Employee TaskedTo { get; set; }
}
I think I've done it correctly, but I have a problem with my Employee class. Usually (When there's only one join) I would simply create virtual collection of Task Property in Employee .. but what am I supposed to do now ? Is this enough to set the relation or should I add virtual properties for these two tasks?
And another thing, when I have an owned entity, with 1-Many relation , is it enough to add the property in the owner entity, and do nothing in the owned one? Or do I have to specify the [Owned] Annotation?
The issue is that you have two one-to-many relationships between the same two entities. For the Employee class you would need two collections, one for each relationship. Additionally, you'll need to use the InverseProperty attribute to tell EF which foreign key goes with which collection:
public class Employee
{
...
[InverseProperty(nameof(Task.RequestedBy))]
public ICollection<Task> RequestedTasks { get; set; }
[InverseProperty(nameof(Task.TaskedTo))]
public ICollection<Task> AssignedTasks { get; set; }
}
You don't need virtual. That's to enable lazy-loading. For the lazy-loading functionality, EF creates a dynamic proxy class that inherits from your entity and overrides the getter on the navigation property. The virtual keyword is required in C# to allow a class member to be overridden.
Also, the Owned attribute is for value objects. It's a way of having a related class whose properties are literally mapped onto the same table or if given its own table, inherently tied to the entity that "owns" it, such that you access that data through the entity, not separately. Neither of which applies here.
Finally, you should reconsider the name Task for this class. .NET already has a Task class, and it's used very frequently. If you name your class Task as well, you'll be having to specify namespaces virtually every time you use either one, which is a pain.

Linking multiple properties to the same table in Entity Framework

Let me foreword this by saying this is my first real experience with both Entity Framework and relational databases in general. If I am doing it completely wrong, please tell me.
I want my data structured as something like this (Cut down on the "extra" code):
Indicators {
int SomeText1TranslationRef
List<Translation> SomeText1Translations
int SomeText2TranslationRef
List<Translation> SomeText2Translations
}
Measures {
int SomeText3TranslationRef
List<Translation> SomeText3Translations
int SomeText3TranslationRef
List<Translation> SomeText4Translations
}
Translation {
Int TranslationID
String LanguageCode
String Text
}
So in essence, the indicators table would have a list of SomeText1 Translations as well as SomeText2, all joined using the TranslationID through the "Ref" properties.
I have the translation properties annotated with [ForeignKey("....Ref")].
I expected this to work magically as the rest of the framework seems to, but instead the translation table gets columns named "SomeText1TranslationRef" and "SomeText2TranslationRef".
Am I doing this wrong?
I am looking at other features of Entity Framework and see an annotation for "InverseProperty". Is it something which may help?
I'm not 100% clear on your goal, but if an Indicator can have many Text1 translations and many Text2 translations, then that is 2 many-to-many relationships. Same for Measures. EF will need a join/bridge/junction table for this (IndicatorTranslation and MeasureTranslation). You can explicitly create this table, or let EF do it behind the scenes:
Indicator {
// other indicator fields
public virtual List<Translation> SomeText1Translations
public virtual List<Translation> SomeText2Translations
}
Measure {
// other measure fields
public virtual List<Translation> SomeText3Translations
public virtual List<Translation> SomeText4Translations
}
Translation {
Int TranslationID
String LanguageCode
String Text
// Use inverse attributes or fluent code to tell EF how to connect relationships
[InverseProperty("SomeText1Translations")]
public virtual ICollection<Indicator> TranslationForIndicatorText1 { get; set; }
[InverseProperty("SomeText2Translations")]
public virtual ICollection<Indicator> TranslationForIndicatorText2 { get; set; }
[InverseProperty("SomeText3Translations")]
public virtual ICollection<Measure> TranslationForMeasureText3 { get; set; }
[InverseProperty("SomeText4Translations")]
public virtual ICollection<Measure> TranslationForMeasureText4 { get; set; }
}
I'm happy to be corrected if I'm wrong since it's nothing I've tried for quite a while, but as far as I'm aware, EF is still not able to create relationships from one property on a type to two different other types, or vice versa, even with constraints that would make it valid.
In your case, you would end up with the 4 navigation properties being required on your translation. (int IndicatorRef1, int IndicatorRef2, int MeasureRef3, int MeasureRef4). Most wouldn't call it a dream scenario.
I asked a similar question a couple of years ago, and have since then sort of concluded that i was foolish trying to get EF to solve all my problems.
So here's an answer to what you're trying to achieve, and perhaps even a solution to 2 of your questions:
Don't rely on EF handle any scenario. Actually, pretty much don't rely on EF to handle relationships at all other than 1-1, 1-* or *-*. And some forms of inheritance.
In most other cases, you will end up with one navigation property for each type you're trying to reference, with data being populated with nulls for each navigation property but the one specifically targeted.
The good news? You don't have to rely on EF for it. The main advantage of EF is it's productivity. For certain cases, it's still worth leveraging EF, but providing your own methods of productivity. If you want to get a set of indicators with 2 collections of translations based on a ref, simply create a method that provides it.
Something like
public IQueryable<Indicators> SetOfIndicatorsWithTranslations()
{
// Untested query that might need some fixing in an actual implementation
return ctx.Set<Indicators>().Select(ind => new Indicators() {
Text1Ref= ind.Text1Ref, // whatever the property is
Text1RefList = ctx.Set<Translation>().Where(t => t.TranslationId == ind.Text1Ref),
Text2Ref= ind.Text2Ref,
Text2RefList = ctx.Set<Translation>().Where(t => t.TranslationId == ind.Text2Ref),
});
}
Now that's a query EF will handle for you gracefully.
There are of course a lot more elegant solutions to something like it. The important part is really that it's sometimes worth doing it yourself rather than restricting yourself to the capabilities of your tool of choice. (Well, at least that's the important part that I eventually learned :) )
Long story short, the caveat is the "Core" part of .Net Core. EF Core does not support convention-over-configuration many-to-many relationships yet (See here).
The only way to achieve this is to manually create the junction tables as Steve suggested. Here is all the information needed: https://www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration
In previous versions of Entity Framework, this model definition was sufficient for EF to imply the correct type of relationship and to generate the join table for it. In EF Core 1.1.0, it is necessary to include an entity in the model to represent the join table, and then add navigation properties to either side of the many-to-many relations that point to the join entity instead:
The above link will most likely be updated with time so for context purposes, here is the code which goes along with it:
public class Book
{
public int BookId { get; set; }
public string Title { get; set; }
public Author Author { get; set; }
public ICollection<BookCategory> BookCategories { get; set; }
}
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public ICollection<BookCategory> BookCategories { get; set; }
}
public class BookCategory
{
public int BookId { get; set; }
public Book Book { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
Alternatively, using Fluent:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<BookCategory>()
.HasKey(bc => new { bc.BookId, bc.CategoryId });
modelBuilder.Entity<BookCategory>()
.HasOne(bc => bc.Book)
.WithMany(b => b.BookCategories)
.HasForeignKey(bc => bc.BookId);
modelBuilder.Entity<BookCategory>()
.HasOne(bc => bc.Category)
.WithMany(c => c.BookCategories)
.HasForeignKey(bc => bc.CategoryId);
}

Entity Framework 6 Fluent Mapping - One to Many Code First

Given the following SQL tables:
EntityGroup:
Id int, (PK)
GroupName nvarchar(100)
Entity:
Id int, (PK)
EntityGroupId int, (FK Non-nullable)
Description nvarchar(100)
And the following POCOs
public class Entity
{
public int Id { get; set; }
public int EntityGroupId { get; set; }
public int RefNumber { get; set; }
}
public class EntityGroup
{
public int Id { get; set; }
public virtual IList<Entity> Entities { get; set; }
}
How do I configure the fluent mapping correctly? I want Entity.EntityGroupId to remain as an int rather than an EntityGroup object.
I want to be able to .Include() optionally Include("Entities"). The closest I got is this, but that seems to eager-load all entities even if I dont use .Include("Entities"), which is not the behaviour I want:
modelBuilder.Entity<EntityGroup>()
.HasMany(x => x.Entities);
You must set off the lazy loading,
you can do this for just a specific unit of work or for all by setting your dbContext Like
dbContext.Configuration.LazyLoadingEnabled = false;
dbContext.Configuration.ProxyCreationEnabled = false;
or set it in Ctor of your DbContext.
The way I understand it, you want to configure one-to-many relationship between EntityGroup and Entity without navigation property in Entity class and using Entity.EntityGroupId as a FK. All that with Fluent API.
It's possible, but you have to start the configuration from the class having a navigation property (EntityGroup in your case) because Has methods require property accessor while With methods have parameterless overloads. As usual, for the last part you will use the HasForeignKey method:
modelBuilder.Entity<EntityGroup>()
.HasMany(e => e.Entities)
.WithRequired()
.HasForeignKey(e => e.EntityGroupId);
But note that EF recognizes the naming convention used in your sample classes (in particular the EntityGroupId), so you'll get the same mapping w/o any fluent configuration or data annotations.
The problem is lazy loading is enabled by default, so it will load the related entities every time you try to get access to them.Two options to solve your issue could be:
Disabling lazy loading in your context:
public YourContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
Removing virtual from your navigation property, which is one of
the requirements to work lazy loading and the tracking change:
public ICollection<Entity> Entities { get; set; }
If you want to learn more about the supported ways you can load related entities in EF I suggest you to read this article

One-to-many relationship in Entity Framework without FK

I inherited a project with all the one to many relationships created in this fashion
[Table("A")]
public partial class A
{
public int Id { get; set; }
public int Something {get; set; }
public virtual ICollection<B> B { get; set; }
}
[Table("B")]
public partial class B
{
[Key]
public int Id { get; set; }
public int Something {get; set; }
[Required]
public virtual A A { get; set; }
}
What struck me was the lack of a int Foreign Key property in the B model.
Entity Framework must create it because they exist in our database.
Can anyone explain first why this is happening and two if this can cause problems with lazy loading?
EntityFramework by default looks for the name "id" and makes it a key. You could specify the decoration to make it faster, since it does not have to guess the key of the table.
I don't believe it affects lazy loading since lazy loading is achieved by creating instances of derived proxy types and then overriding virtual properties to add the loading hook
Sources:
Microsoft Key
Microsoft Lazy Loading
Foreign key properties are not required by the EF. EF can build hidden fields basing on object relations (and I usually use this configuration because I think is more POCO). The only issue is that in some cases, the exception raised during validation or SaveChanges is more cryptic, otherwise everything works fine.
About foreign key column names, you can configure them using fluent api (Map + MapLeftKey, MapRightKey and MapKey methods).

Fluent NHibernate - Cascade delete a child object when no explicit parent->child relationship exists in the model

I've got an application that keeps track of (for the sake of an example) what drinks are available at a given restaurant. My domain model looks like:
class Restaurant {
public IEnumerable<RestaurantDrink> GetRestaurantDrinks() { ... }
//other various properties
}
class RestaurantDrink {
public Restaurant Restaurant { get; set; }
public Drink { get; set; }
public string DrinkVariety { get; set; } //"fountain drink", "bottled", etc.
//other various properties
}
class Drink {
public string Name { get; set; }
public string Manufacturer { get; set; }
//other various properties
}
My db schema is (I hope) about what you'd expect; "RestaurantDrinks" is essentially a mapping table between Restaurants and Drinks with some extra properties (like "DrinkVariety" tacked on).
Using Fluent NHibernate to set up mappings, I've set up a "HasMany" relationship from Restaurants to RestaurantDrinks that causes the latter to be deleted when its parent Restaurant is deleted.
My question is, given that "Drink" does not have any property on it that explicitly references RestaurantDrinks (the relationship only exists in the underlying database), can I set up a mapping that will cause RestaurantDrinks to be deleted if their associated Drink is deleted?
Update: I've been trying to set up the mapping from the "RestaurantDrink" end of things using
References(x => x.Drink)
.Column("DrinkId")
.Cascade.All();
But this doesn't seem to work (I still get an FK violation when deleting a Drink).
The answers to this question suggest that what I want to do isn't possible: how to define an inverse cascade delete on a many-to-one mapping in hibernate

Categories

Resources