Should I have the foreign key column in my entity models? - c#

I am coming from the Entity Framework over to NHibernate. When looking at how to create my domain entities I noticed that in some of the examples they don't include the column of the foreign key relationship. Since the Session class contains a Load() method it is possible to just use objects without the trip to the database instead of primary keys. Is this a normal practice when constructing entity models in NHibernate.
Example Entity
public class BlogPost : Entity
{
public virtual string Name { get; set; }
//Should this be here
public virtual int AuthorID { get; set; }
public virtual Author Author { get; set; }
}
Creating Entity
BlogPost post = new BlogPost
{
Name = "My first post",
Author = session.Load<Author>(1) //Avoids hitting the database
};
session.Save(post);
-- OR ---
BlogPost post = new BlogPost
{
Name = "My first post",
AuthorID = 1 //Use the id of the object
};
session.Save(post);

You should use full entities / objects instead of having foreign keys.
Foreign keys are database concept. They don't make much sense when you are doing object oriented programming. When doing OOP, you are composing objects together. In your case, a Blog has a collection of Posts. Post has a parent Blog, etc.
Entity IDs are just used to uniquely identify entities.
The whole point of Object-Relational Mapping should be to allow you to use OOP best practices (Object), database best practices (Relational) and not to mix the concepts between them (that's what Mapping part of the name stands for).
Some ORMs are better than others in following this. Hint: NHibernate.

Related

understanding entity framework core foreign key relationships

Im utilsing a code first approach for the first time (Ive previously always used database first) and am trying to understand some basic concepts. If I create a foreign key relationship between two entities, how does entity framework know which properties (columns) to use in the two sides of the relationship ? My question is probably better explained with a simple code example. I have two entities, patient and treatment. A patient can have multiple treatments so there will be a one to many relationship between the patient and the treatment, with a foreign key relationship existing between the two entities. Here are my entity classes. Please note these are greatly simplified for the sake of explanation.
public class Patient
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<PatientTreatment> PatientTreatment { get; set; }
}
public class PatientTreatment
{
public int Id { get; set; }
public string TreatmentDescription { get; set; }
public int PatientId { get; set; }
public virtual Patient Patient { get; set; }
}
So for the patient entity the primary key would be Id and for the PatientTreatment entity, its primary key would also be Id
For the foreign key relationship, according to what Ive googled so far, the code above will create that relationship for me, is this correct ? If so, how would entity framework know that the PatientId in PatientTreatment is linked to Id in the Patient entity ? This is how its supposed to be in the database (SQL Server), but I cant see how entity framework would know this. Im really new to the code first approach so Im just trying to understand how this would work. Could anyone explain this to me ?
Ive also read that setting the relationship as above doesnt create indexes (PatientId in PatientTreatment) so these have to be created in code as well
EF works with conventions, as Caius mentioned.
In your case:
EF knows that there are two entity object - Patient and PatientTreatment, because dbSet and optional configuration exist for those classes.
Patient contains so called navigation property leading to PatientTreatment's - a collection, but it could be most of the things implementing IEnumerable - EF assumes that You want to create relationship here.
Patient have an Id field - EF by naming convention without any configuration will assume that this is an entity key. Same goes for PatientTreatment
PatientTreatment has a navigation property to a single Patient - this, again, by convention tells EF that you want the relationship between this two entities to be one-to-many - collection on one side, single reference on the other side.
Ofc one to many could also be possible by convention even without navigation property in PatientTreatment - just to be clear.

One to many relationship doesn`t retrieve data in entity framework

I`m in process of learning C# & .NET and EF (with aspnetboilerplate) and I came up with idea to create some dummy project so I can practice. But last 4 hour Im stuck with this error and hope someone here can help me.
What I create( well at least I think I create it correctly ) is 2 class called "Ingredient" and "Master"
I want to use it for categorize Ingredient with "Master" class.
For example ingredient like
Chicken breast
chicken drumstick
Both of them belong to Meat ( witch is input in "Master" database ) and here is my code
Ingredient.cs
public class Ingrident : Entity
{
public string Name { get; set; }
public Master Master { get; set; }
public int MasterId { get; set; }
}
Master.cs
public class Master : Entity
{
public string Name { get; set; }
public List<Ingrident> Ingridents { get; set; } = new();
}
IngridientAppService.cs
public List<IngridientDto> GetIngWithParent()
{
var result = _ingRepository.GetAllIncluding(x => x.Master);
//Also I try this but doesn`t work
// var result = _ingRepository.GetAll().Where(x => x.MasterId == x.Master.Id);
return ObjectMapper.Map<List<IngridientDto>>(result);
}
IngridientDto.cs
[AutoMap(typeof(IndexIngrident.Entities.Ingrident))]
public class IngridientDto : EntityDto
{
public string Name { get; set; }
public List<MasterDto> Master { get; set; }
public int MasterId { get; set; }
}
MasterDto.cs
[AutoMap(typeof(IndexIngrident.Entities.Master))]
public class MasterDto : EntityDto
{
public string Name { get; set; }
}
When I created ( for last practice ) M -> M relationship this approach with .getAllIncluding work but now when I have One -> Many it won`t work.
Hope someone will be able to help me or at least give me some good hint.
Have a nice day !
Straight up the examples you are probably referring to (regarding the repository etc.) are overcomplicated and for most cases, not what you'd want to implement.
The first issue I see is that while your entities are set up for a 1-to-many relationship from Master to Ingredients, your DTOs are set up from Ingredient to Masters which definitely won't map properly.
Start with the simplest thing. Get rid of the Repository and get rid of the DTOs. I'm not sure what the base class "Entity" does, but I'm guessing it exposes a common key property called "Id". For starters I'd probably ditch that as well. When it comes to primary keys there are typically two naming approaches, every table uses a PK called "Id", or each table uses a PK with the TableName suffixed with "Id". I.e. "Id" vs. "IngredientId". Personally I find the second option makes it very clear when pairing FKs and PKs given they'd have the same name.
When it comes to representing relationships through navigation properties one important detail is ensuring navigation properties are linked to their respective FK properties if present, or better, use shadow properties for the FKs.
For example with your Ingredient table, getting rid of the Entity base class:
[Table("Ingredients")]
public class Ingredient : Entity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int IngredientId { get; set; }
public string Name { get; set; }
public int MasterId { get; set; }
[ForeignKey("MasterId")]
public virtual Master Master { get; set; }
}
This example uses EF attributes to aid in telling EF how to resolve the entity properties to respective tables and columns, as well as the relationship between Ingredient and Master. EF can work much of this out by convention, but it's good to understand and apply it explicitly because eventually you will come across situations where convention doesn't work as you expect.
Identifying the (Primary)Key and indicating it is an Identity column also tells EF to expect that the database will populate the PK automatically. (Highly recommended)
On the Master side we do something similar:
[Table("Masters")]
public class Master : Entity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int MasterId { get; set; }
public string Name { get; set; }
[InverseProperty("Master")]
public virtual ICollection<Ingredient> Ingredients { get; set; } = new List<Ingredient>();
}
Again we denote the Primary Key, and for our Ingredients collection, we tell EF what property on the other side (Ingredient) it should use to associate to this Master's list of Ingredients using the InverseProperty attribute.
Attributes are just one option to set up the relationships etc. The other options are to use configuration classes that implement IEntityConfiguration<TEntity> (EF Core), or to configure them as part of the OnModelCreating event in the DbContext. That last option I would only recommend for very small projects as it can start to become a bit of a God method quickly. You can split it up into calls to various private methods, but you may as well just use IEntityConfiguration classes then.
Now when you go to fetch Ingredients with it's Master, or a Master with its Ingredients:
using (var context = new AppDbContext())
{
var ingredients = context.Ingredients
.Include(x => x.Master)
.Where(x => x.Master.Name.Contains("chicken"))
.ToList();
// or
var masters = context.Master
.Include(x => x.Ingredients)
.Where(x => x.Name.Contains("chicken"))
.ToList();
// ...
}
Repository patterns are a more advanced concept that have a few good reasons to implement, but for the most part they are not necessary and an anti-pattern within EF implementations. I consider Generic repositories to always be an anti-pattern for EF implementations. I.e. Repository<Ingredient> The main reason not to use repositories, especially Generic repositories with EF is that you are automatically increasing the complexity of your implementation and/or crippling the capabilities that EF can bring to your solution. As you see from working with your example, simply getting across an eager load through to the repository means writing in complex Expression<Func<TEntity>> parameters, and that just covers eager loading. Supporting projection, pagination, sorting, etc. adds even more boiler-plate complexity or limits your solution and performance without these capabilities that EF can provide out of the box.
Some good reasons to consider studying up on repository implementations /w EF:
Facilitate unit testing. (Repositories are easier to mock than DbContexts/DbSets)
Centralizing low-level data rules such as tenancy, soft deletes, and authorization.
Some bad (albeit very common) reasons to consider repositories:
Abstracting code from references or knowledge of the dependency on EF.
Abstracting the code so that EF could be substituted out.
Projecting to DTOs or ViewModels is an important aspect to building efficient and secure solutions with EF. It's not clear what "ObjectMapper" is, whether it is an Automapper Mapper instance or something else. I would highly recommend starting to grasp projection by using Linq's Select syntax to fill in a desired DTO from the models. The first key difference when using Projection properly is that when you project an object graph, you do not need to worry about eager loading related entities. Any related entity / property referenced in your projection (Select) will automatically be loaded as necessary. Later, if you want to leverage a tool like Automapper to help remove the clutter of Select statements, you will want to configure your mapping configuration then use Automapper's ProjectTo method rather than Map. ProjectTo works with EF's IQueryable implementation to resolve your mapping down to the SQL just like Select does, where Map would need to return everything eager loaded in order to populate related data. ProjectTo and Select can result in more efficient queries that can better take advantage of indexing than Eager Loading entire object graphs. (Less data over the wire between database and server/app) Map is still very useful such as scenarios where you want to copy values back from a DTO into a loaded entity.
Do it like this
public class Ingrident:Entity
{
public string Name { get; set; }
[ForeignKey(nameof(MasterId))]
public Master Master { get; set; }
public int MasterId { get; set; }
}

Flatten composite entities

Is it possible to flatten a two-table relationships into a single entity in Entity Framework?
Specifically, (simplified for example) given the following two tables that define a 1-1 relationship
create table Foo
(
Id int not null identity (1, 1)
constraint PK_Foo_Id primary key (Id),
Name nvarchar(64) not null,
BarId int not null
constraint FK_Bar_Foo foreign key (BarId) references Bar (Id)
)
create table Bar
(
Id int not null identity (1, 1)
constraint PK_Bar_Id primary key (Id),
Value nvarchar(max) not null
)
I can easily map this to entities like this
public class Foo
{
public int Id { get; set;}
public string Name { get; set;}
public Bar Bar { get; set;}
}
public class Bar
{
public int Id { get; set;}
public string Value { get; set;}
}
But what I would like to map to a single flattened entity
public class FlatFoo
{
public int Id { get; set;}
public string Name { get; set;}
public string Value { get; set;}
}
Notice that only one field from table Bar is mapped to FlatFoo
Notes
The actual tables are larger.
Since the text value in Bar can get large it would fill index pages quickly, so there are two tables for quicker index searches against Foo.Id and Foo.Name.
I have looked into Split Entities, but it required both tables have the same primary key.
I have looked at Complex Types but it works in the opposite manner taking a flat table and splitting into composite entities.
I am looking to use the Fluent API to perform the mapping.
Can you provide any help in flattening the mapping between two tables and a single entity?
Update
Yes, views will work to get a flat entity, but then I am not mapping from tables to entity. Likewise, from the other side, I know it is possible to map to non-public composition and expose the property that way. But, I am more interested in learning if EF fluent API is flexible enough to handle the mapping directly than I am in solving a particular issue.
Unfortunately, there is considerable push-back here (at work) to any suggestion of adding anything other than tables to a database (something as basic as views included). It is typically pointed out that doing so adds additional point of maintenance, increases training for support, adds complexity for basic CRUD and other excuses for not learning the tools available. It is silly at best, but it is something I have to deal with. :(
So, as a point of learning for me, is it possible to do this seemingly basic task of directly mapping fields from two arbitrary tables into one entity using EF, fluent API preferred?
Entity Framework doesn't provide a way to map one entity to two tables and then cherry pick from the columns in the way you describe unless the tables share a common key. So as mentioned in the comments, the simplest solution is to create a View and map the entity to that.
public class FlatFooMap : EntityTypeConfiguration<FlatFoo>
{
public FlatFooMap ()
{
ToTable("vwFlatFoo");
HasKey(t => t.Id);
}
}

Define Many Tables to One Table relationship in Code First approach

I am in the process of building up a data model in Entity Framework using the Code First approach, but one part has me a bit stumped. The title on this question may be a bit confusing, so I will explain my problem in detail. The length of this post may be daunting, but I think it's a fairly straightforward problem.
I have one model defined like this:
public class KeyValuePair
{
[Key]
[MaxLength(128)]
[Column(Order = 0)]
public virtual string OwnerId { get; set; }
[Key]
[MaxLength(128)]
[Column(Order = 1)]
public virtual string Key { get; set; }
public virtual string Value { get; set; }
}
My intent is for this to just define a generic table for storing key-value properties on other entities in the system. I am using GUIDs for all of my Ids, so OwnerId should uniquely refer to one entity in the system, and the pair (OwnerId, Key) should uniquely identify one property on one entity.
In other words, I want to allow multiple tables in my system to have a One->Many relationship to this KeyValuePair table.
So for example, if I wanted to store the height of a Person who has the ID b4fc3e9a-2081-4989-b016-08ddd9f73db0, I would store a row in this table as:
OwnerId = "b4fc3e9a-2081-4989-b016-08ddd9f73db0"
Key = "Height"
Value = "70 in."
So now I want to define navigation properties from the parent entities to this table, like (to take the Person example):
public class Person
{
[Key]
public virtual string Id { get; set; }
public virtual string Name { get; set; }
// I want this to be a navigation property
public ICollection<KeyValuePair> Properties { get; set; }
}
But I'm not sure how do define the relationship between Person and KeyValuePair so that Entity Framework knows that it should look up the Person's properties by matching the Person's Id against the KeyValuePairs' OwnerId. I can't define a foreign key in the KeyValuePair model, because the OwnerId is going to refer to Ids in several different tables.
It looks like I can do the following to define a relationship from Person to KeyValuePair in OnModelCreating:
modelBuilder.Entity<Person>()
.HasMany(p => p.Properties).WithMany().Map(mp =>
{
mp.MapLeftKey("Id");
mp.MapRightKey("OwnerId", "Key");
mp.ToTable("PersonDetail");
});
Or I could even give the KeyValuePairs their own unique IDs, get rid of OwnerId, and do this:
modelBuilder.Entity<Person>()
.HasMany(p => p.Properties).WithMany().Map(mp =>
{
mp.MapLeftKey("Id");
mp.MapRightKey("Id");
mp.ToTable("PersonDetail");
});
But both of these approaches involve the creation of an intermediary table to link the Person and KeyValuePair tables, and that seems like excessive overhead in terms of bloating my database schema and requiring more expensive JOINs to query the data.
So is there a way to define the relationship such that I don't need to involve intermediary tables? Am I going about this database design the wrong way?
Side note: For anyone wondering why I am using this approach to define properties on my entities rather than simply adding fixed properties to the data model, I am using fixed properties in the data model where applicable, but the application I am building requires the ability to define custom properties at runtime. I also think this question is applicable to other potential scenarios where multiple tables have a One->Many relationship to a shared table.
The only way I can think of doing it (and I'll admit, this is not the best of ideas, but it will do what you're asking) would be to have any classes that need to have this relationship with KeyValuePair implement an abstract class that contains the fully implemented navigational property, as well as the ID field. By "fully implemented" I don't mean an actual, mapped relationship; I mean that it should use a DbContext to go out to the KeyValuePair table and actually grab the relevant properties given the ID.
Something like this:
public abstract class HasKeyValuePairs
{
[Key]
public virtual string Id { get; set; }
[NotMapped]
public ICollection<KeyValuePair> Properties
{
get
{
using(var db = new DbContext())
{
return db.KeyValuePairs.Where(kvp => kvp.OwnerID == this.ID);
}
}
}
}
Assuming you're using Lazy Loading (given that you're using the virtual keyword), there shouldn't be much extra overhead to doing it like this, since EF would have to go back to the database anyway to pick up the properties if you ever called for them. You might need to have that return a List just to avoid any potential ContextDisposedException later on in your code, but that at least will get you up and running.

EF4.1 (code first) - How to specify a composite relationship

In Linq to SQL I could specify a relationship that didn't have to depend on the foreign keys and pks existing in the database, useful for creating composite relationships like this:
public class Equipment_CableNormalised
{
...
[Association(ThisKey = "EquipmentId,PortNumber", OtherKey = "EquipmentId,PortNumber", IsForeignKey = false)]
public List<EquipmentPort> EquipmentPorts
{
get; set;
}
}
This then generated the sql similar to " .. join EquipmentPorts EP on EP.EquipmentId = blah and EP.PortNumber = Blah".
Can I do the same sort of thing in EF4.1 (using annotations or fluent api)? I know you can specify composite keys and use the [Keys] and [ForeignKeys] attributes, but this relationship doesn't map to keys...
How does the sample relation from your code works? I expect that EquipementId must be either PK or unique key (not supported in both L2S and EF) on one side because otherwise the relation could not exist (both one-to-one and one-to-many demands unique principal). Once it is PK on one side the port number is redundant.
Code first allows only mapping to keys. If you have existing database you can cheat it in your model and map new relations in the same way as you would map existing but you still have to follow simple rule - properties in principal are primary keys, properties in dependent entity are mapped as foreign keys.
If you want EF to generate DB for you, you will always have all relations in the database.
Use HasKey http://www.ienablemuch.com/2011/06/mapping-class-to-database-view-with.html
Either use HasKey, put this on OnModelCreating
 modelBuilder.Entity<SalesOnEachCountry>().HasKey(x => new { x.CountryId, x.OrYear });   
Or use Key Column Order
public class SalesOnEachCountry
{       
    [Key, Column(Order=0)] public int CountryId { get; set; }
    public string CountryName { get; set; }
    [Key, Column(Order=1)] public int OrYear { get; set; }
     
    public long SalesCount { get; set; }     
    public decimal TotalSales { get; set; }
}
Regarding your question about foreign key, I haven't yet tried the pure code(OnModelCreating) approach, perhaps you can just put two ForeignKey attribute on child class itself, might need to put Column Order too.
This could be the answer composite key as foreign key
That answer confirms my hunch that you could put two ForeignKey attributes on child class itself.

Categories