I am trying to do this:
Hibernate: Parent-Child Relationship to Itself
but in ASP.NET MVC 4
here are my 2 models (which i have no clue if this is the correct way to do this):
public class Group
{
public int GroupID { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
public virtual ICollection<GroupRelation> GroupRelations { get; set; }
}
public class GroupRelation
{
public int GroupRelationID { get; set; }
public int? ParentID { get; set; }
public int? ChildID { get; set; }
[ForeignKey("ParentID")]
public virtual Group ParentGroups { get; set; }
[ForeignKey("ChildID")]
public virtual Group ChildGroups { get; set; }
}
Here is my Context(again no clue if this is right):
public class TaskTrackerContext : DbContext
{
public DbSet<Group> Groups { get; set; }
public DbSet<GroupRelation> GroupRelations { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Group>()
.HasMany(g => g.GroupRelations).WithOptional(g => g.ChildGroups).HasForeignKey(g => g.ChildID);
modelBuilder.Entity<Group>()
.HasMany(g => g.GroupRelations).WithOptional(g => g.ParentGroups).HasForeignKey(g => g.ParentID);
}
}
With this setup i get the following in my database:
(i tried to put in a pic or a link to a pic and it wont let me...)
So it makes the 2 relationships i am expecting "PK_GroupID - FK_ParentID" and "PK_GroupID - FK_ChildID", but then it creates an extra column called "Group_GroupID" and makes the following relationship: "PK_GroupID - FK_Group_GroupID".
So what am I doing wrong?
Related
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.
I designed three entities with navigation properties (code first approach, unnecessary properties are omitted):
Image (represents image info in database):
public class Image
{
public long Id { get; init; }
public long? PlayerId { get; set; }
public Player Player { get; set; }
public long? PublicationId { get; set; }
public Publication Publication { get; set; }
}
Publication (some domain entity):
public class Publication
{
public long Id { get; init; }
public long? ImageTitleId { get; set; }
public Image ImageTitle { get; set; }
public long? ImageBackgroundId { get; set; }
public Image ImageBackground { get; set; }
}
Player (some domain entity):
public class Player
{
public long Id { get; init; }
public long? ImageProfileId { get; set; }
public Image ImageProfile { get; set; }
public long? ImageLogoId { get; set; }
public Image ImageLogo { get; set; }
}
Business rules are:
Image is dependent entity in all cases
Publication/Player is principal entity in these relationships
Publication/Player can have related image or not (one-to-one-or-zero)
The difficulty for me here is multiple relationships between two entities (therefore tables in database) (Title Image, Background Image, etc).
EF can not automatically detect principal/dependent entity and i can not figure out how to config Fluent API in such case.
I think you missed some properties in Image Entity.
I changed Image model as below:
public class Image
{
public long Id { get; init; }
public long? ImageProfilePlayerId { get; set; }
public Player ImageProfilePlayer { get; set; }
public long? ImageLogoPlayerId { get; set; }
public Player ImageLogoPlayer { get; set; }
public long? ImageTitlePublicationId { get; set; }
public Publication ImageTitlePublication { get; set; }
public long? ImageBackgroundId { get; set; }
public Publication ImageBackground { get; set; }
}
and Paley and Publication as below (they are same as yours):
public class Publication
{
public long Id { get; init; }
public long? ImageTitleId { get; set; }
public Image ImageTitle { get; set; }
public long? ImageBackgroundId { get; set; }
public Image ImageBackground { get; set; }
}
public class Player
{
public long Id { get; init; }
public long? ImageProfileId { get; set; }
public Image ImageProfile { get; set; }
public long? ImageLogoId { get; set; }
public Image ImageLogo { get; set; }
}
and used below config for models in OnModelCreating in DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Image>(e => {
e.HasOne(x => x.ImageProfilePlayer).WithOne(x => x.ImageProfile).HasForeignKey<Player>(x => x.ImageProfileId).IsRequired(false);
e.HasOne(x => x.ImageLogoPlayer).WithOne(x => x.ImageLogo).HasForeignKey<Player>(x => x.ImageLogoId).IsRequired(false);
e.HasOne(x => x.ImageTitlePublication).WithOne(x => x.ImageTitle).HasForeignKey<Publication>(x => x.ImageTitleId).IsRequired(false);
e.HasOne(x => x.ImageBackground).WithOne(x => x.ImageBackground).HasForeignKey<Publication>(x => x.ImageBackgroundId).IsRequired(false);
});
}
the result is :
I'm trying to make a nested collection search and I'm really struggling.
My expected result is: I would like to make a search and find all the powerUp objects by a certain date. (PowerUpDate property - that's the searching criteria)
User Model:
public class AppUser : IdentityUser
{
public ICollection<Hero> Heroes { get; set; }
}
Hero Model:
[Table("Heroes")]
public class Hero
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Ability { get; set; }
[Required]
public string SuitColors { get; set; }
public double CurrentPower { get; set; }
public double StartingPower { get; set; }
public DateTime Created { get; set; } = DateTime.Now;
public ICollection<PowerUp> PowerUps { get; set; }
public AppUser AppUser { get; set; }
[Required]
public string AppUserId { get; set; }
}
PowerUp Model:
[Table("PowerUps")]
public class PowerUp
{
public int Id { get; set; }
[Required]
public double PowerUpIncrement { get; set; }
[Required]
public DateTime PowerUpDate { get; set; } = DateTime.Now;
public Hero Hero { get; set; }
[Required]
public int HeroId { get; set; }
}
DataContext:
public class DataContext : IdentityDbContext<AppUser>
{
public DataContext(DbContextOptions options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Hero>().HasMany(hero => hero.PowerUps).WithOne(powerUp => powerUp.Hero)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<AppUser>().HasMany(user => user.Heroes).WithOne(hero => hero.AppUser)
.OnDelete(DeleteBehavior.Cascade);
}
}
Could someone please explain to me how can I implement such a search on a nested collection?
Inject your AppUser user using Dependency injection
(better use the repository pattern) anyway it should be something like this: user.Heroes.PowerUps.OrderBy(x=>x.PowerUpDate == Datetime.Now).ToList();
x.PowerUpDate == To whatever date you will insert
I am trying to map properties of the same type on a OneToMany association. I tried to distinguish with Description but kinda stuck here.
public class User
{
public virtual int UserId { get; set; }
public virtual string UserName { get; set; }
[Description("From")]
public virtual IList<Message> FromMessageList { get; set; }
[Description("To")]
public virtual IList<Message> ToMessageList { get; set; }
}
public class Message
{
public virtual int MessageId { get; set; }
public virtual string Text { get; set; }
[Description("From")]
public virtual User FromUser { get; set; }
[Description("To")]
public virtual User ToUser { get; set; }
}
public class DefaultHasManyConvention : IHasManyConvention
{
public void Apply(IOneToManyCollectionInstance instance)
{
if (instance.OtherSide.Property.GetDescription() == instance.Member.GetDescription())
{
if (instance.Member.GetDescription() != null)
instance.Key.Column(instance.Member.GetDescription() + "Id");
else
instance.Key.Column(instance.OtherSide.Property.Name + "Id");
instance.Fetch.Select();
}
}
}
public class DefaultReferenceConvention : IReferenceConvention
{
public void Apply(IManyToOneInstance instance)
{
if (instance.Property.GetDescription() != null)
instance.Column(instance.Property.GetDescription() + "Id");
else
instance.Column(instance.Property.Name + "Id");
instance.Fetch.Select();
}
}
For one to many relationships I generally use coding like :
public class User
{
public int UserId { get; set; }
public string UserName { get; set; }
[Description("From")]
public virtual ICollection<Message> FromMessageList { get; set; }
[Description("To")]
public virtual ICollection<Message> ToMessageList { get; set; }
}
public class Message
{
public int MessageId { get; set; }
public string Text { get; set; }
[Description("From")]
public virtual User FromUser { get; set; }
// From user foreign key column
[ForeignKey("FromUser")]
public int FromUserId {get;set;}
[Description("To")]
public virtual User ToUser { get; set; }
// ToUser foreign key column
[ForeignKey("ToUser")]
public int ToUserId {get;set;}
}
Try to use ICollection instead of IList - this solved many issues for me.
Add foreign key column names; it makes mapping simpler and filtering in queries easier.
I have several cases where my navigation property is repeated in a class, or I don't want it to have the same name as the class it represents. Example:
public class Employee
{
public Guid EmployeeId { get; set; }
public string Name { get; set; }
}
public class Project
{
public Guid ProjectId { get; set; }
public DateTime Start { get; set; }
public Guid ResponsibleId { get; set; }
public virtual Employee Responsible { get; set; }
public Guid OwnerId { get; set; }
public virtual Employee Owner { get; set; }
}
When using EF Code First, it messes up the foreign keys, creates new ones with different names. How do I handle this?
Thanks a lot!
This should help:
public class Employee
{
public Guid EmployeeId { get; set; }
public string Name { get; set; }
public virtual ICollection<Project> ResponsibleProjects { get; set; }
public virtual ICollection<Project> OwnedProjects { get; set; }
}
public class Project
{
public Guid ProjectId { get; set; }
public DateTime Start { get; set; }
public Guid ResponsibleId { get; set; }
public virtual Employee Responsible { get; set; }
public Guid OwnerId { get; set; }
public virtual Employee Owner { get; set; }
}
public YourContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Project>()
.HasRequired(p => p.Owner)
.WithMany(e => e.OwnedProjects )
.HasForeignKey(p => p.OwnerId );
modelBuilder.Entity<Project>()
.HasRequired(p => p.Responsible)
.WithMany(e => e.ResponsibleProjects )
.HasForeignKey(p => p.ResponsibleId );
base.OnModelCreating(modelBuilder);
}
Note how relations are defined for both ends and how I point what exact field is used as a foreign key.
I ended up doing this, which works:
public class Employee
{
public Guid EmployeeId { get; set; }
public string Name { get; set; }
}
public class Project
{
public Guid ProjectId { get; set; }
public DateTime Start { get; set; }
public Guid ResponsibleId { get; set; }
[ForeignKey("ResponsibleId")]
public virtual Employee Responsible { get; set; }
public Guid OwnerId { get; set; }
[ForeignKey("OwnerId")]
public virtual Employee Owner { get; set; }
}
Thanks to Wiktor for the comment that lead to the solution.