Let's say in our project we use C# and MsSQL and we have one Products table with two columns (ID,Name)
One day we decided to save product information given by Company1, so we created a new table ProductInfoFromCompany1 because it has custom columns (ProductID, Price, CurrentScore)
The next day, we agreed with Company2 and now we need to save their data as well. So, new table -> ProductInfoFromCompany2 with different columns (ProductID, Year, Rating)
Another day, we agreed with Company3 and so on...
So, we have no idea how the data given by new companies will look like. That's why we need to create a new table because if we use one Details table, it will be too wide with numerous null columns
In Entity Framework Core we have these models:
public class ProductInfoFromCompany1
{
public int Id { get; set; }
public int ProductId { get; set; }
public decimal Price { get; set; }
public double CurrentScore { get; set; }
public Product Product { get; set; }
}
public class ProductInfoFromCompany2
{
public int Id { get; set; }
public int ProductId { get; set; }
public int Year { get; set; }
public double Rating { get; set; }
public Product Product { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
//Do we need these navigation properties in this class?
//public ProductInfoFromCompany1 ProductInfoFromCompany1 { get; set; }
//public ProductInfoFromCompany2 ProductInfoFromCompany2 { get; set; }
}
You can see my question is commented in the Product class.
Do we need to add navigation properties in the Product class?
The reason why I'm asking is that in all books or documentation which I've read, people use navigation property, but in this case, it violates open-closed principle because whenever we add new company, we need to modify Product class as well.
P.S. if we want to query ProductInfoFromCompany1 data and we have product Id, we can simply start querying from ProductInfoFromCompany1, like this
var info = _db.ProductInfoesFromCompany1.Where(c=>c.ProductId == productId);
Do we need to add navigation properties in the Product class?
You are the only one who can answer the question if you need something or not.
If the question is does EF Core require navigation properties, the answer is no. Reference: Relationships - Single Navigation Property EF Core documentation topic:
Including just one navigation property (no inverse navigation, and no foreign key property) is enough to have a relationship defined by convention.
In fact EF Core fluent API and shadow properties allow defining relationship without any navigation or FK property. How useful it would be is another story. The main point (which is the question as I read it) is that none of them is mandatory.
Of course the lack of a navigation property imposes some limitations on the type of LINQ queries you can create - like you said, you can't start a query from Product and apply filter on associated ProductInfoFromCompany1, or eager/explicit/lazy load it.
But if you don't need all that, e.g. as you said, you can build your queries starting from ProductInfoFromCompany1, then omitting the navigation property in Product is perfectly fine.
As I mentioned in my comment a design change is required to achieve what you want.
Here is my suggestion:
Since your issue is with the structure of the product table because you don't know what each company wants to store as info for their product you can do it this way : (I ll explain later).
public class Company
{
[Key]
public int Id { get; set; }
[Display(Name = "Name")]
[Required]
public string Name { get; set; }
[Display(Name = "Description")]
public string Description { get; set; }
[Required]
[Display(Name = "Created date")]
[DataType(DataType.DateTime)]
public DateTime CreatedDate { get; set; }
public virtual ICollection<Product> Prodcuts { get; set; }
}
public class Product
{
[Key]
public int Id { get; set; }
[Display(Name="Name")]
[Required]
public string Name { get; set; }
[Required]
[Display(Name = "Created date")]
[DataType(DataType.DateTime)]
public DateTime CreatedDate { get; set; }
[Required]
[ForeignKey("Company")]
[Display(Name = "Company")]
public int CompanyID { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<ProductField> Fields { get; set; }
}
public class ProductField
{
[Key]
public int Id { get; set; }
[Display(Name = "Value")]
[Required]
public string Value { get; set; }
[Required]
[ForeignKey("Product")]
[Display(Name = "Product")]
public int ProductID { get; set; }
public virtual Product Product { get; set; }
[Required]
[ForeignKey("Field")]
[Display(Name = "Field")]
public int FieldID { get; set; }
public virtual Field Field { get; set; }
[Required]
[Display(Name = "Created date")]
[DataType(DataType.DateTime)]
public DateTime CreatedDate { get; set; }
}
public class Field
{
[Key]
public int ID { get; set; }
[MaxLength(100)]
[Index("ActiveAndUnique", 1, IsUnique = true)]
[Required]
[Display(Name = "Name")]
public string Name { get; set; }
[Display(Name = "Description")]
public string Description { get; set; }
[Required]
[Display(Name = "Created date")]
[DataType(DataType.DateTime)]
public DateTime CreatedDate { get; set; }
}
Explanation of the code:
This approach gives you more control over your data without having to create a table for each product info.
Company:
I started by creating a company table with a navigation property that will lazy load all the products related to it.(if lazy loading is enabled)
Then In the product table I added a FK to reference the company.
Field:
Since you mentioned that you don't know what a company will have as product info , you can create a new field and link it to a product using the ProductField table .
ProductField:
This table will act as a "Many to Many" between your product, and field as a result you can add as many field to a new product without having to modify the structure of your product table or create a new one . You can also reuse the same field if company number 3 needs it.
USAGE:
Given we have a company named MyCompany.
MyCompany has a product named Car and the info required to be added to the car is Make, and Color.
We create two new fields called Make, and Color, then in the ProductField Table we add two new entries:
The first one will have:
The ID of the field "Make", The value "BMW", and a reference to the product with its id which is Car.
We do the same thing for color by referencing the the field "Color" and the product "Car".
Querying:
Now querying is simpler than having a table for each company product info.
Example:
var myProducts = _db.Products.Where(p=>p.CompanyID== "1").Include(p=>p.Fields).Tolist()
Again that's my take on it. Hope it helps.
Related
Department and store model. They are a one to one relationship. when I do the (Include) only some models it brings me the complete data.
The data is correctly in the database
var orders = await _context.Order.Include(o => o.Departament)
.Include(o => o.Store).ToArrayAsync();
Model Order
public class Order
{ [Key]
public string Order_Id { get; set; }
[DataType(DataType.Date)]
public DateTime Date { get; set; }
public string Number_Order { get; set; }
public int Qty { get; set; }
[DataType(DataType.DateTime)]
public DateTime Imported { get; set; }
[DataType(DataType.DateTime)]
public DateTime Modified { get; set; }
[DataType(DataType.DateTime)]
public DateTime Exported { get; set; }
public bool IsActive { get; set; }
[ForeignKey("Departament")]
public string DepartmentId { get; set; }
[ForeignKey("Store")]
public string StoreId { get; set; }
public virtual ICollection<OrderDetail> OrderDetails { get; set; }
public Departament Departament { get; set; }
public Store Store { get; set; }
}
.
I just need to solve that problem of getting the data correctly.
what do u mean one to one!!! i think your design is one to many.
as Steve Py said in his comment ## The relationship between Order->Department and Order->Store is not One-to-One, it is Many-to-One. (Many orders could refer to the same Department) ##
use virtual keyword for navigation properties.
accroding to https://stackoverflow.com/a/41881299/18399373
you can use virtual keyword to load related data in lazy way
use explicit loading and check it.
Entity Framework supports three ways to load related data - eager loading, lazy loading and explicit loading
I want to know if there's a way to add a validator where an author needs to be selected on a combobox otherwise it will display an error.
In my application I have three models, Books, Authors and the join table Rel_Book_Author.
public class Book
{
[Key]
[Display(Name = "Id")]
[Column("book_id")]
public int book_id { get; set; }
[Display(Name = "Title")]
[Column("liv_title")]
[Required(ErrorMessage = "Every book needs a title")]
public string liv_title { get; set; }
}
public class Author
{
[Key]
[Display(Name = "Id")]
[Column("aut_id")]
public int aut_id { get; set; }
[Display(Name = "Author's Name")]
[Column("aut_name")]
public string aut_name { get; set; }
public ICollection<Rel_Book_Author> BookAuthors { get; set; }
}
public class Rel_Book_Author
{
[Column("hla_aut_id")]
public int aut_id { get; set; }
public Author Author { get; set; }
[Column("hla_book_id")]
public int book_id { get; set; }
public Book Book { get; set; }
}
I'm making the assumption here that your view's model will be the Rel_Book_Author class, or a list of those classes, and so you the view shows a book (or books) and allows the user to pick the author from a list for each book?
If so, then validation should work as per any other model with data annotations.
EF Core does not perform any validations itself, it expects you to do have already validated the object using the client-side validation and on the server (typically in the controller), so the fact that the objects are related by a particular type of relationship (e.g. many-to-many) doesn't matter here.
There is one gotcha to watch out for with the Required attribute for an integer; which is that a non-nullable integer (obviously) cannot be null, it will just default to zero, which means that setting Required won't actually ever return a validation error for an integer in a select list (as the property will always have a value of zero or the value of whatever is selected in the list).
To get round that, declare the aut_id property as nullable (int?):
[Required]
public int? aut_id { get; set; }
or add a Range attribute, e.g.
[Range(1, int.MaxValue)]
public int? aut_id { get; set; }
I'm testing the following scenario (code below): create a shopping mall, with shops, with cashiers, with cash desks and persons operating it.
So I've created the following classes
Table Mall
Table Shop
Table CashDesk
Table Person
Basic classes with an ID and a name.
Then I need a derived class from Person being PersonCashier or PersonCustomer.
Since everything is related, I need to create intersection tables for the many to many scenarios.
I've created the following intersection tables:
The Mall contains shops: MallShop
The shop contains CashDesks: MallShopCashDesk
And the CashDesk containing cashiers: MallShopCashDeskPersonCashier
This just doesnt feel right. Can anyone help me out on a best practice
public class Mall
{
public int Id { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
[Required]
public int NumberOfShopSpaces { get; set; }
}
public class Shop
{
public int Id { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
}
public class CashDesk
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
public class Person
{
public int Id { get; set; }
[Required]
public string FullName { get; set; }
[Required]
public string Gender { get; set; }
}
public class PersonCashier : Person
{
[Required]
public int ShopId { get; set; }
public virtual Shop Shop { get; set; }
}
These are the base classes. How should i add a shop to a mall, a cashdesk to a shop, and a cashier to a cashdesk? (i've tried alot but posting the code would make the question look like spaghetti)
If this is code first then EF will make the relationship tables for you. You just need to add the relationships in your classes.
Exactly what they should be is not clear from your description because it depends on what relationships you want; if you want a many-many between PersonCashier and CashDesk it would be like this:
public class CashDesk
{
public List<PersonCashier> Cashiers { get; set; }
}
public class PersonCashier : Person
{
public List<CashDesk> CashDesks { get; set; }
}
(not showing all the fields for brevity)
public class Mall
{
public int Id { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
[Required]
public int NumberOfShopSpaces { get; set; }
List<Shop> CurrentShops { get; set; }
}
public class Shop
{
public int Id { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
public List<CashDesk> CashDesks { get;set; }
}
I added the list of shops to the Mall class and CashDesks to Shops. This gives you a list of shops in the mall, and a list of cashdesks in each shop and you can follow this method for everything else you need.
If you have a database, you will have a Mall table and a Shop table.
The Shop table can have a Foreign Key to the Mall table, that's how you link them and this will work with the class structure at the top.
Or, you can have another table called MallShops where you have 2 fields, one being the MallID, the other the ShopID. This is called a Link table.
Both approaches will work with the second allowing a more complex structure with lots of Malls linked to Lots of Shops.
I would start with the database structure first, make sure you cover all you need, then you can do the classes etc in a way that makes sense. If you use something like EntityFramework then it will create all the classes for you once your database structure is ready.
I'm trying to implement inheritance using entity framework 6.0 and database first approach. OK, let's say I have a Person and an Organization entity like below:
// a simplified version of organization entity
public class Organization
{
public Guid ID { get; set; }
public string Nickname { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public string OfficialName { get; set; }
public Guid CEOID { get; set; }
public DateTime? RegisterDate { get; set; }
}
// a simplified version of person entity
public class Person
{
public Guid ID { get; set; }
public string Nickname { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public Guid PersonID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleName { get; set; }
public string NationalCode { get; set; }
public DateTime? BirthDate { get; set; }
}
I can create these two tables in database, but I want to use inheritance so the fields which is repeated in both Person and Organization could be in another base class like below:
public class Identity
{
// These fields are the common fields between Person and Organization
public Guid ID { get; set; }
public string Nickname { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
}
How can I achieve this in db-first approach?
One possible way is to use one table for each type called TPT (table-per-type), which I prefer to use. To achieve this, you define your tables like the model shown in the following picture:
Note that the relationships between child and base entity are one-to-one on their pk columns, and all common fields are moved to the base table. After creating your tables, right click on the models page in your visual studio, and select Update Model from Database..., and then in the add tab, select these 3 tables to add. At first you should see this model diagram, which needs to be changed a bit:
Do these steps for Person and Organization separately:
Right click on entity and select Properties
In the Base Type property select Identity
Select and then delete the association between this entity and Identity
Select and then Delete the PK (ID column) of this entity (Inherits from base entity)
After these steps save your model. Now your model should look like this:
Now compile your project and enjoy your life!
Additional resources:
Entity Framework Designer TPH Inheritance
public class Product
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public bool IsInStock { get; set; }
public string ImageUrl { get; set; }
public List<ProductOption> ProductOptions { get; set; }
public virtual Category Category { get; set; }
}
public class ProductOption
{
[Key]
public int Id { get; set; }
public string ProductOptionName { get; set; }
public string ProductOptionDescription { get; set; }
}
Now I know when your using Code First EF, so that the tables are created correctly. You need to do something like this.
modelBuilder.Entity<Product>().HasMany(p => p.ProductOptions).WithMany().Map(m =>
{
m.MapLeftKey("ProductId").MapRightKey("ProductOptionId").ToTable("SelectedProductOptionsInOrderedItem");
});
So....
Does this mean that if I do something like Product.ProductOptions I will be able to access all associated productoptions.
Is this the best way to set it up, or is there another way?
To enable lazy load and EF can create derived proxy types for your collection, that property should be declared this way:
public virtual ICollection<ProductOptions> ProductOptions { get; set; }
That should be enought. Other aspect is the mapping approach that you use. You choose fluent api, i prefer mapping by convention, but that is a matter of personal taste anyway.
Ok, Mapping by Conventions:
Is the ability of EF that from the name of entities and their properties along with their types, to map our model with the underlying data without providing any other information.
for example
public class Customer {
public long CustomerID {get; September;}
public string CustomerName {get; September;}
public Employee AssignedTo {get; September;}
}
With the previous model EF will map database with a table named Customer with:
. CustomerID bigint primary key column
. CustomerName nvarchar column
. Customer_EmployeeID foreign key to Employee table, with the datatype Corresponding to EmployeeID in that table.
You can read more Here