How to establish one-to-many relationship for a code-first approach? - sql

I'm trying to build a recipe app for my spouse. I'm trying to set it up so she can add new recipes to the database as the app grows.
When adding new recipe, she will have three drop-down to pick from to construct her new recipe ingredients. First one will contain a list of ingredients that she can choose from, the second one a list of measuring units and the third one a list of quantities.
Here is what I got so far. Am I heading in the right direction or am I off? I'm using Entity Framework with a code-first approach:
public class Recipes
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Image { get; set; }
}
public class Units model
{
[Key]
public int Id { get; set; }
public string UnitName { get; set; }
}
public class UnitQty
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
public class IngredientsModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
public class RecipeIngredients
{
public int Id { get; set; }
public int RecipesId { get; set; }
[ForeignKey("RecipesId")]
public Recipes Recipes { get; set; }
public int IngredientsModelId { get; set; }
[ForeignKey("IngredientsModelId")]
public IngredientsModel IngredientsModel { get; set; }
public int UnitQtyId { get; set; }
[ForeignKey("UnitQtyId")]
public UnitQty UnitQty { get; set; }
public int UnitsModelId { get; set; }
[ForeignKey("UnitsModelId")]
public UnitsModel UnitsModel { get; set; }
}
After creating the table, controller and the views, this is what I get in the recipe ingredients index view.
Any suggestion will be more than welcome please and thank you
RecipeIngredient class's view

First of all. You are over engineering your domain model. On relational databases Join is bottleneck you should prevent from joins if it doesn't helps you.
public class Recipt
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Image { get; set; }
public ICollection<RecipeIngredient> Ingredients { get; set; }
}
public class IngredientModel
{
public int Id { get; set; }
public string Name { get; set; }
public IngredientUnit UnitType { get; set; } // Unit model is best to be added here. if it doesn't change in a single IngredientModel.
}
public class RecipeIngredient
{
public int Id { get; set; }
public int UnitQuantiy { get; set; } // No need to more classes.
public IngredientModel Model { get; set; }
public Recipt Recipt { get; set; }
}
public Enum IngredientUnitType // Same Unit Model but less database relation as its small finite collection.
{
Killogram,
Count,
....
}
and according to the Microsoft documents its best to use fluentApi configuration for the relations.
Override this method in your Context:
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Recipt>.HasMany(P => P.Ingredients).WithOne(P => P.Recipt);
builder.Entity<RecipeIngredient>.HasOne(P => P.Model);
// There is no need to explicit foreign key definition. but you can explicitly define your foreign keys.
}
And for the last part. in Views you can use extra models called ViewModels.
As above domain turned to a minimal domain you just need to pass a list of IngredientModels to your view to complete your View.

Related

How to configure One to Many relationship in Entity Framework

I am creating API in ASP .NET Core that will retrieve posts with user Id. Post should contain text and Id of a user who posted it.
I have two models Users and Posts and I need help on how to configure this relationship
I want one User to have many posts
Currently my user model contains
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public List<Post> Posts { get; set; }
}
And my Post model
public class Post
{
public int Id { get; set; }
public string Text { get; set; }
}
What is the best way to do this ?
One to many relationships ( User to have many posts).
public class User{
public int Id { get; set; }
public string Username { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int Id { get; set; }
public string Text { get; set; }
//Navigation
public int UserId { get; set; }
public User User{ get; set; }
}
this is your Model Class:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public Virtual List<Post> Posts { get; set; }
}
public class Post
{
public int Id { get; set; }
public string Text { get; set; }
public int UserId { get; set; }
public Virtual User User { get; set; }
}
and in your DbContext:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// configures one-to-many relationship
modelBuilder.Entity<User>().HasMany(x=>x.Posts).WithRequired(x=>x.User)
.HasForeignKey<int>(s => s.UserId);
}

Doubts about EF Core 2.1 Relations

I am working on Entity Framework Core Code First approach and ASP.Net Core 2.1 making 3 tables:
Person class
public class Person
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public PeopleProfessions PeopleProfessions { get; set; }
}
Professions' class
public class Profession
{
public string Id { get; set; }
public string Name{ get; set; }
public PeopleProfessions PeopleProfessions { get; set; }
}
peopleprofessions' class
public class peopleprofessions
{
[ForeignKey("PersonId ")]
public string PersonId { get; set; }
public ICollection<Person> People { get; set; }
[ForeignKey("ProfessionId")]
public string ProfessionId{ get; set; }
public ICollection<Profession> Professions { get; set; }
}
On my Context:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<peopleprofessions>().HasKey(up => new { up.PersonId, up.ProfessionId });
}
Bearing this in mind:
People can have multiple professions.
The professions table is only for reading stored data like "Accountant".
I have doubts about how I can make table 3 only contain the foreigners and that it can meet the needs that I just mentioned.
I have tried to make the relationship appropriately but I also noticed that in tables 1 and 2 it requests both Id of the table people's professions.
I don't know if I am lost or if I am looking wrong or if there is an alternative to that situation. Thanks for any help you can give me.
You have the use of Collections on the navigation items a bit backwards. For your primary entities (Person and Profession), they should have collections, since it's one-to-many. But for the PeopleProfessions, each record is a single link to a specific entity, so no collection there just a direct object reference.
public class Person
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<PeopleProfessions> PeopleProfessions { get; set; }
}
public class Profession
{
public string Id { get; set; }
public string Name{ get; set; }
public ICollection<PeopleProfessions> PeopleProfessions { get; set; }
}
public class PeopleProfessions
{
public string PersonId { get; set; }
public Person Person { get; set; }
public string ProfessionId { get; set; }
public Profession Profession { get; set; }
}
You can, but don't need to specify a ForeignKey attribute because you are following EFs naming conventions(it will figure it out for you). Your OnModelCreating looks correct for the composite key.
You may want to consider removing the plural from PeopleProfessions (just call the class PeopleProfession) since one instance represents a single People-Profession relationship. I typically do this and but the navigation name in the entities remains plural, since it can represent more than one, i.e.
public ICollection<PeopleProfession> PeopleProfessions { get; set; }

Querying a table that relates to multiple entity types

So currently I have an application model of:
Note -> Thing
(A note can relate to many things)
A thing can be numerous entities (for this example lets use Computer & Car)
e.g
Note -> Computer
-> Car
So right now, I have the schema of
Note -> ComputerNote -> Computer
Note -> CarNote -> Car
The problem is that because the entity links are in separate tables, it requires a new query to be written rather than just using filtering in the WHERE clause.
Ideally it would be nice to have an EntityId & EntityTypeId column & on the Note table that would hold the primary key of the related entity and the type of the entity. Thus application logic could look for all Car notes where the car is x without a separate query for each type, but.. this would mean I lose referential integrity. Is there a better way, or is what I have suggested an acceptable design?
Entity Framework Model's:
public partial class Note
{
public Note()
{
NoteComputer = new HashSet<NoteComputer>();
NoteCar = new HashSet<NoteCar>();
NoteThing = new HashSet<NoteThing>();
}
public int Id { get; set; }
public string Value { get; set; }
public string CreatedByUserId { get; set; }
public DateTime CreatedDateTime { get; set; }
public ICollection<NoteComputer> NoteComputer { get; set; }
public ICollection<NoteCar> NoteCar { get; set; }
public ICollection<NoteThing> NoteThing { get; set; }
}
public partial class NoteCar
{
public int Id { get; set; }
public int NoteId { get; set; }
public int CarId { get; set; }
public Car Car { get; set; }
public Note Note { get; set; }
}
public partial class NoteComputer
{
public int Id { get; set; }
public int NoteId { get; set; }
public int ComputerId { get; set; }
public Computer Computer { get; set; }
public Note Note { get; set; }
}
public partial class NoteThing
{
public int Id { get; set; }
public int NoteId { get; set; }
public int ThingId { get; set; }
public Thing Thing { get; set; }
public Note Note { get; set; }
}
As there seems to be no nice way to handle this at the database level, I've found it best to handle this at the application level using the concrete type database schema, to produce dynamic joins.
Example in Entity Framework Core:
public GenericEntityProvider
{
private readonly IEnumerable<IEntityProvider> _entityProviders;
private readonly DatabaseContext _context;
public GenericEntityProvider(IEnumerable<IEntityProvider> entityProviders, DatabaseContext context)
{
_entityProviders = entityProviders;
_context = context;
}
public IEnumerable<Note> Get(Type type, int id) {
var provider = _entityProviders.GetPredicate(type, id);
return _context.Notes.Where(provider);
}
}
public CarNoteProvider : IEntityProvider
{
public Expression<Func<Note, bool>> GetPredicate(Type type, int id)
{
return x => x.CarNote.Any(cn => cn.CarId == id);
}
}

Is it possible to create a Domain Class which has Multiple FK Columns to same PK?

I'm a newbie to designing database.
I have problem how to define a domain class which has multiple foreign keys linked with a same primary key.
Here is my model:
namespace OceanFmsSystem.Domain
{
public class ExportTemplate
{
public int Id { get; set; }
public List<ExportBooking> ExportBookings { get; set; }
public string TemplateName { get; set; }
public int CustomerId { get; set; }
public string Incoterms { get; set; }
public string IncotermsDetail { get; set; }
public string PaymentTerm{ get; set; }
public int CountryOriginId { get; set; }
public int CountryDestinationId { get; set; }
}
}
What I want to do is that CountryOriginId & CountryDestinationId should refer to the below class as foreign keys:
namespace OceanFmsSystem.Domain
{
public class Country
{
public int Id { get; set; }
public string CountryCode { get; set; }
public string CountryName { get; set; }
}
}
As far as I know, in EF Core there is an convention which I should name a foreign key as below for migration from code to database.
public type ClassNameOfPrimaryKeyId { get; set;}
Is there any possible way to make this happens?
Yes, possible. Your class should look like this:
public class ExportTemplate
{
//...
public int CountryOriginId { get; set; }
public Country CountryOrigin { get; set; }
public int CountryDestinationId { get; set; }
public Country CountryDestination { get; set; }
}
EF is smart enough to figure the Ids by convention. If you do not wish to follow the convention you can use [ForeignKey] attribute on the properties to configure the FK:
[ForeignKey("Origin")]
public int MyOriginId { get; set; }
public Country Origin { get; set; }

EF Code First not setting foreign key in one to one relationship

So I'm trying to create a simple Product-Preview 1 to 1 relationship as follows:
public class Product : BaseEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string name { get; set; }
public virtual EPS eps { get; set; }
public virtual Preview preview { get; set; }
[ForeignKey("userId")]
public virtual User user { get; set; }
public Guid userId { get; set; }
}
and
public class Preview : BaseEntity
{
[Key,ForeignKey("Product")]
public Guid Id { get; set; }
public string imagePath { get; set; }
public double width { get; set; }
public double height { get; set; }
public virtual List<TextPreview> Texts { get; set; }
public virtual List<ImagePlaceHolder> ImagePlaceHolders { get; set; }
[ForeignKey("ProductId")]
public virtual Product Product { get; set; }
public virtual Guid ProductId { get; set; }
}
I was expecting to have a foreign key in the Previews table that would point to a Product
but after running the migration I just get it as regular field
What I'm I doing wrong?
You almost have it you just missed one piece of the puzzle ...
[ForeignKey("ProductId")]
public virtual Product Product { get; set; }
You also need to add ...
public Guid ProductId { get; set; }
to the preview object.
It's also worth noting that the ForeignKey attrib can be put on either property, and the string must refer to the other one of the pair.
As it's currently written you are trying to make the Id property specify the value for both the primary key and the foreign key on the tables in question.
So your final code might look something like ...
public class Product : BaseEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[ForeignKey("User")]
public Guid UserId { get; set; }
public string name { get; set; }
public virtual EPS eps { get; set; }
public virtual Preview preview { get; set; }
public virtual User user { get; set; }
}
and
public class Preview : BaseEntity
{
[Key]
public Guid Id { get; set; }
[ForeignKey("Product")]
public Guid ProductId { get; set; }
public string imagePath { get; set; }
public double width { get; set; }
public double height { get; set; }
public virtual List<TextPreview> Texts { get; set; }
public virtual List<ImagePlaceHolder> ImagePlaceHolders { get; set; }
public virtual Product Product { get; set; }
}
As a side note I would also recommend against using concrete collection types like List<T> instead use something like IList<T> or ICollection<T> it promotes better code reuse and extensibility.