Automapper and EF Navigation Properties - asp.net-core

With ASP.NET MVC Core and Entity Framework Core I'm trying to create a simple website.
I've defined my Model:
public class Club
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual IEnumerable<Team> Teams { get; set; }
}
public class Team
{
[Key]
public int Id { get; set; }
public int ClubId { get; set; }
[MaxLength(32)]
public string Name { get; set; }
public virtual Club Club { get; set; }
}
As well as the corresponding View Models:
public class ClubViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public virtual IEnumerable<TeamViewModel> Teams { get; set; }
}
public class TeamViewModel
{
public int Id { get; set; }
public int ClubId { get; set; }
public string Name { get; set; }
public virtual ClubViewModel Club { get; set; }
}
I've defined an Automapper Profile with the corresponding mappers:
CreateMap<Club, ClubViewModel>();
CreateMap<ClubViewModel, Club>();
CreateMap<Team, TeamViewModel>();
CreateMap<TeamViewModel, Team>();
I try to load a Club entity, with the navigation property Teams included (_context.Club.Include(c => c.Teams).ToList()). This works as expected, it returns a Club with a list of Teams. But when I try to map this instance to a ClubViewModel, I get an 502.3 error and my debug session is ended immediately.
It seems like I am missing something trivial, but I simply do not see it. There's no information in the Windows Event Log and I can't find any usefull information in the IIS Express logging (%userprofile%\documents\IISExpress)
What is causing the crash?

You can't perform this mapping because it is circular. You'll have to remove this line
public virtual ClubViewModel Club { get; set; }
from your TeamViewModel and the mapping should work as expected.

Related

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

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.

Can I reference single foreign key on multiple columns in a table? If yes, how to configure it in Entity Framework core

How can I generate above table like structure using Entity Framework core?
I am using code first approach to generate my tables from domain models which are as follows
public class Contact
{
public int ContactId { get; set; }
public string Email { get; set; }
public string Name { get; set; }
}
public class Company
{
public int CompanyId { get; set; }
public string CompanyName { get; set; }
[ForeignKey("Contact")]
public int FirstContact { get; set; }
[ForeignKey("Contact")]
public int SecondContact { get; set; }
[ForeignKey("Contact")]
public int ThirdContact { get; set; }
public virtual Contact Contact { get; set; }
}
In the company table, I want foreign key on columns 'FirstContact', 'SecondContact', 'ThirdContact' which can refer to Contacts table.
I have also tried Fluent API but no success in same also.
Whenever I am running the add-migration command, I am getting this error:
There are multiple properties with the [ForeignKey] attribute pointing to navigation 'Company.Contact'. To define a composite foreign key using data annotations, use the [ForeignKey] attribute on the navigation.
I would appreciate any help regarding the same.
Thanks.
For multiple Navigation Properties, you need multiple Foreign Keys. EG
public class Company
{
public int CompanyId { get; set; }
public string CompanyName { get; set; }
public virtual Contact FirstContact { get; set; }
public virtual Contact SecondContact { get; set; }
public virtual Contact ThirdContact { get; set; }
}
And let EF Core create shadow properties for your FKs or with Foreign Key properties:
public class Company
{
public int CompanyId { get; set; }
public string CompanyName { get; set; }
public int FirstCotactId {get; set;}
public virtual Contact FirstContact { get; set; }
public int SecondCotactId {get; set;}
public virtual Contact SecondContact { get; set; }
public int SecondCotactId {get; set;}
public virtual Contact ThirdContact { get; set; }
}

'FK_Ratings_Users_UserId' on table 'Ratings' may cause cycles or multiple cascade paths

I am trying in .NET EFCore the following Code-First migrations through the entities below
User
[Table("Users")]
public class User
{
[Key]
public int Id { get; set; }
[Required]
[MaxLength(100)]
public string FirstName { get; set; }
[Required]
[MaxLength(100)]
public string LastName { get; set; }
[Required]
[MaxLength(250)]
public string Email { get; set; }
[Required]
[MinLength(8), MaxLength(16)]
public string Password { get; set; }
[Required]
[MinLength(6), MaxLength(15)]
public string Phone { get; set; }
public ICollection<Apartment> Apartments { get; set; }
public ICollection<Rating> Ratings { get; set; }
}
Apartment
[Table("Apartments")]
public class Apartment
{
[Key]
public int Id { get; set; }
[Required]
[MinLength(24), MaxLength(100)]
public string Title { get; set; }
[Required]
[MinLength(24), MaxLength(250)]
public string Address { get; set; }
[Required]
public int Price { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }
public User User {get; set;}
public ICollection<Rating> Ratings { get; set; }
public ICollection<AptCateg> AptsCategs { get; set; }
}
Ratings
[Table("Ratings")]
public class Rating
{
[Key]
public int Id { get; set; }
public int Value { get; set; }
[ForeignKey("Apartment")]
public int ApartmentId { get; set; }
public Apartment Apartment { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }
public User User { get; set; }
}
I use the commands dotnet ef migrations add InitialDatabase but when I try to use dotnet ef database update it throws the following error in cmd, as in the title
'FK_Ratings_Users_UserId' on table 'Ratings' may cause cycles or
multiple cascade paths
I tried adding as in the EFCore tutorial from here the modelBuilder's Cascade behavior but it doesn't work because I am getting the same error. I have also tried doing the answer from here but the implementation for HasRequired isn't working even if try to install EntityFrameworkCore.Tools.
I understand that there is an issue with a circular thingy going on. From my intuition the program doesn't know what to do in the case of deleting a user, if to drop or not its ratings and apartments or some of that sort, and this is why its acting this way but I can't fix the problem.
My question is, how can I solve this issue as I cannot create my database, and thus I cannot continue working on the project.
Thanks!
You'll have to make the user relationship optional on one of the tables like:
public int? UserId { get; set; }
Making the property type nullable tells EF that a cascade delete is not required here.
You are causing a cyclic reference by adding the User and Apartment to the Ratings entity. User and Apartment entities already have a one-to-many relationship to the Ratings collection.
'FK_Ratings_Users_UserId' on table 'Ratings' may cause cycles or
multiple cascade paths
This is how the Ratings entity should look like:
[Table("Ratings")]
public class Rating
{
[Key]
public int Id { get; set; }
public int Value { get; set; }
}

ASP.NET MVC/EF Code First error: Unable to retrieve metadata for model

I'm trying to create a controller in MVC4 and I'm getting an error I don't understand (I'm new to MVC). It says "Unable to retrieve metadata for 'CIT.ViewModels.DashboardViewModel'..." and then gives 2 possible problems. One is that the DashboardViewModel has no key defined. The other is that EntitySet 'DashboardViewModels' has no key defined.
I defined a key for DashboardViewModel, but that didn't solve the problem. Here is my DashboardViewModel;
public class DashboardViewModel
{
public DashboardViewModel() { }
[Key]
public int Id { get; set; }
public Hardware Hardware { get; set; }
public Software Software { get; set; }
public HardwareType HardwareType { get; set; }
public Manufacturer Manufacturer { get; set; }
public SoftwarePublisher SoftwarePublisher { get; set; }
}
As you can see it is composed of classes. I did this so I could have multiple classes accessible from the same view. I didn't think it needed a key, but I added one and that didn't fix the problem. The other error sounded like it was looking for a DbSet for DashboardViewModels. As I understand it, your DbSets are your tables. I don't want or need a DashboardViewModels table. I'm only doing that so I can have multiple tables/classes accessible in my view. That's working fine up to this point.
When I am trying to create the controller, I am using the DashboardViewModel as as my model and Context as my context. Here is my context:
public class Context : DbContext
{
public DbSet<Software> Softwares { get; set; }
public DbSet<Location> Locations { get; set; }
public DbSet<SoftwarePublisher> SoftwarePublishers { get; set; }
public DbSet<SoftwareType> SoftwareTypes { get; set; }
public DbSet<Hardware> Hardwares { get; set; }
public DbSet<Manufacturer> Manufacturers { get; set; }
public DbSet<HardwareType> HardwareTypes { get; set; }
}
How do I address these errors?

Fluent NHibernate Mapping Non Required Object using Automapping

I have a composite object set up Project->Appraisal, My appraisal object has a ApprovedMentor object which is not required but when i go to save project Nhib throws and error to say that ApprovedUser has not been set. but its not set because its not a required field. How do i set up this using fluent auto mapping, is it possible?
public class MentoringProject : BaseEntity
{
public MentoringProject()
{
Appraisal = new Appraisal();
}
[NotNullNotEmpty]
[Length(Min=25, Max=1000)]
public virtual string Description { get; set; }
[Length(Min=25, Max=1000)]
public virtual string SupportRequired { get; set; }
[NotNullNotEmpty]
public virtual System.DateTime? DateSubmitted { get; set; }
[NotNullNotEmpty]
public virtual System.DateTime? ClosingDate { get; set; }
[NotNullNotEmpty]
[Size(Min=1)]
public virtual short Duration { get; set; }
[NotNullNotEmpty]
public virtual string Skills { get; set; }
public virtual Appraisal Appraisal { get; set; }
}
public class Appraisal : BaseEntity
{
public Appraisal()
{
ShortlistedMentors = new List<User>();
ApprovedMentor = new User();
College = new RefData();
}
#region Primitive Properties
public virtual bool Decision { get; set; }
public virtual System.DateTime? ApprovedDate { get; set; }
public virtual System.DateTime? AcceptedDate { get; set; }
public virtual System.DateTime? CompletionTargetDate { get; set; }
public virtual string RejectionReason { get; set; }
#endregion
#region Navigation Properties
public virtual IList<User> ShortlistedMentors { get; set; }
public virtual User ApprovedMentor { get; set; }
public virtual RefData College { get; set; }
#endregion
}
It looks to me that you just want to ignore the ShortlistedMentors property which you need to do in your mapping class like this:
map.IgnoreProperty(p => p.ShortlistedMentors);
This answer was posted in this question.
I think i have solved this, when binding the UI to the controller in MVC, MVC creates an empty User object and because that object has required fields set on it using nhib validator and nhib was trying to create a new user object, I got round this by checking if there is a user realtionship to add, if not I set the Appraisal.ApprovedMentor==null