Automapper generates left join in SQL - sql

I'm using Automapper in my .NET Core 2.2 back-end service. I use ProjectTo method to generate my result.
This is my Product class:
public class Product
{
public DateTime? SellEndDate { get; set; }
public string Name { get; set; }
public bool AllowToShow { get; set; }
public Category Category { get; set; }
public Guid CategoryId { get; set; }
public Brand Brand { get; set; }
public Guid BrandId { get; set; }
}
And it is the result class:
public class Dto
{
public string Name { get; set; }
public CategoryDto Category { get; set;}
public BrandDto Brand { get; set; }
}
The configuration in the profile is a normal one. The generated SQL statement for category table has Inner Join, but for brand table it uses a Left Join.
Why is that?

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.

How to show and search a list of products in asp.net core?

I would like to build these functionalities to a project using Asp.Net Core MVC.
Could someone please guide me through, How I can approach these steps:
View a list of product types for a given product category or for all categories.
I have created an ASP.NET Core MVC project with Identity authentication, where the user could register and log in.
I also have these Models created.
namespace Company.Models
{
public class ProductType
{
public ProductType()
{
Products = new List<Product>();
}
public long ProductTypeId { get; set; }
public string ProductName { get; set; }
public string ProductInfo { get; set; }
public string Location { get; set; }
public ProductTypeStatus Status { get; set; }
public string ImageUrl { get; set; }
public string Manufacturer { get; set; }
public string AdminComment { get; set; }
public Category Categories { get; set; }
public ICollection<Product> Products { get; protected set; }
}
public enum ProductTypeStatus
{
Available,
ReservedAdmin
}
public enum ProductStatus
{
Available,
ReservedLoaner,
ReservedAdmin,
Loaned,
Defect,
Trashed,
Lost,
NeverReturned
}
namespace Company.Models
{
public class Product
{
public long ProductId { get; set; }
public long ProductTypeId { get; set; }
public int ProductNumber { get; set; }
public string SerialNo { get; set; }
public ProductStatus Status { get; set; }
public string AdminComment { get; set; }
public string UserComment { get; set; }
public long? CurrentLoanInformationId { get; set; }
}
}
namespace Company.Models
{
public class Category
{
public Category()
{
ProductTypes = new List<ProductType>();
}
public int CategoryId { get; set; }
public string Name { get; set; }
public ICollection<ProductType> ProductTypes
{
get; protected set;
}
}
I have recently turned to Asp.Net Core MVC. So this is a new envirnoment for me to get startd. Though, I did follow the msdn tutorials on asp.net mvc.
I APPRECIATE any help!
I saw your model design I think you missing 1 small thing that is relationship between Product and Category.
1 Product will be in 1 Category
So to add 1 to 1 relationship you need to adjust your model like this. You can view more here
namespace Company.Models
{
public class Product
{
public long ProductId { get; set; }
public long ProductTypeId { get; set; }
public int ProductNumber { get; set; }
public string SerialNo { get; set; }
public ProductStatus Status { get; set; }
public string AdminComment { get; set; }
public string UserComment { get; set; }
public long? CurrentLoanInformationId { get; set; }
public Category Category { get;set; }
}
}
namespace Company.Models
{
public class Category
{
public Category()
{
ProductTypes = new List<ProductType>();
}
public int CategoryId { get; set; }
public string Name { get; set; }
public ICollection<ProductType> ProductTypes
{
get; protected set;
}
}
}
So when you update your model you will need to run ef migration to apply change to db. Detail can be found here
And finally you need to write the code to query some thing like
var query = _db.Product.Where(x => x.Category == "Book");
You can read how to write ef query in c# here

Asp Core Multiple Entity Relationships

I am working on modeling a Contact Info Structure and haven't quite figured out how the relationships should be coded with EF Core. I am fairly new to using EF for data access layer.
I want to have a contact model which can contain Website, Phonenumbers, Emails, or Social Info. Then the contact info will be added to several different models. Any suggestions would be helpful, I am not sure how code this One to many with many table relationship or if it is even possible using EF.
Models so far
public class Contact
{
public String Id { get; set; }
public Int32 ContactType { get; set; } //Enum for Website, Phonenumbers, Emails, or Social
public String RecId { get; set; } //FK to multiple Models
public String RecType { get; set; }//Value for which model the RecID is for
public String Name { get; set; }
public String Value { get; set; }
}
public class ContactInfo
{
public virtual IList<Contact> Website { get; set; }
public virtual IList<Contact> PhoneNumbers { get; set; }
public virtual IList<Contact> Emails { get; set; }
public virtual IList<Contact> Socials { get; set; }
}
//Example of models to use the contact model
public class Company
{
....
pubic ContactInfo ContactInfo { get; set;}
}
public class Client
{
....
pubic ContactInfo ContactInfo { get; set;}
}
If I understand your question correctly, then you could use following code sample, but it is not exactly what you are trying to achieve. This may give you some understanding what you need to do with EF.
public class Contact
{
public String Id { get; set; }
public ContactType ContactType { get; set; } //Enum for Website, Phonenumbers, Emails, or Social
public String RecId { get; set; } //FK to multiple Models (This can't be the FK to multiple table as it should be FK for one table so that FK for Company would be CompanyId, FK for the Client should ClientId)
public String RecType { get; set; }//Value for which model the RecID is for (This need to rethink as it may not needed.)
public String Name { get; set; }
public String Value { get; set; }
// One to Many Relationship
public string CompanyId? { get; set; }
public string ClientId? { get; set; }
public Company Company { get; set; }
public Client Client { get; set; }
}
public class Company
{
public String Id { get; set; }
// Other properties
// One to Many Relationship
public ICollection<Contact> Contacts { get; set; }
}
public class Client
{
public String Id { get; set; }
// Other properties
// One to Many Relationship
public ICollection<Contact> Contacts { get; set; }
}
/* Db context */
public class YourDbContext : DbContext
{
public YourDbContext(DbContextOptions<YourDbContext> options)
: base(options)
{
}
public virtual DbSet<Contact> Contacts { get; set; }
public virtual DbSet<Company> Companies { get; set; }
public virtual DbSet<Client> Clients { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Contact>().HasKey(t => t.Id);
modelBuilder.Entity<Company>().HasKey(t => t.Id);
modelBuilder.Entity<Company>().HasMany(c => c.Contacts).WithOne(c => c.Company).HasForeignKey(k => k.CompanyId);
modelBuilder.Entity<Client>().HasKey(t => t.Id);
modelBuilder.Entity<Client>().HasMany(t => t.Contacts).WithOne(c =>c.Client).HasForeignKey(k => k.ClientId);
}
}
/* Db context - Endd */
public enum ContactType
{
Website,
PhoneNumbers,
Emails,
Social
}
Let me know if you need anymore information.
With the help from DSR, this is the solution I have (untested).
public class Company
{
public String Id { get; set; }
public String Name { get; set; }
public ICollection<ContactPhone> PhoneNumbers { get; set; }
public ICollection<ContactEmail> ContactEmail { get; set; }
public ICollection<ContactWebsite> ContactWebsite { get; set; }
public ICollection<ContactSocial> ContactSocial { get; set; }
}
public class Client
{
public String Id { get; set; }
public String Name { get; set; }
public ICollection<ContactPhone> PhoneNumbers { get; set; }
public ICollection<ContactEmail> ContactEmail { get; set; }
public ICollection<ContactWebsite> ContactWebsite { get; set; }
public ICollection<ContactSocial> ContactSocial { get; set; }
}
public class ContactWebsite
{
public String Id { get; set; }
public String Url { get; set; }
public Company Company { get; set; }
public Client Client { get; set; }
}
public class ContactPhone
{
public String Id { get; set; }
public String Type { get; set; }
public String Number { get; set; }
public Company Company { get; set; }
public Client Client { get; set; }
}
public class ContactEmail
{
public String Id { get; set; }
public String Category { get; set; }
public String Email { get; set; }
public Company Company { get; set; }
public Client Client { get; set; }
}
public class ContactSocial
{
public String Id { get; set; }
public String Site { get; set; }
public String Handle { get; set; }
public Company Company { get; set; }
public Client Client { 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.

Handling document relationships with T[] instead of T using RavenDB

RavenDB docs show how to deal with document relationships in this sample using Includes.
public class Order
{
public Product[] Items { get; set; }
public string CustomerId { get; set; }
public double TotalPrice { get; set; }
}
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
public string[] Images { get; set; }
public double Price { get; set; }
}
public class Customer
{
public string Name { get; set; }
public string Address { get; set; }
public short Age { get; set; }
public string HashedPassword { get; set; }
}
How would I deal with Includes or Live Projections if I don't want to include the customer using Includes/Live Projections but a list of products instead:
public class Order
{
public string[] ItemIds { get; set; }
public string CustomerId { get; set; }
public double TotalPrice { get; set; }
}
If I understand what you're asking, this should help. I blogged about it here:
http://inaspiralarray.blogspot.com/2012/03/keeping-domain-model-pure-with-ravendb.html
Does that help?