Entity Framework - Complex Model using Code First - entity-framework-4.1

Good day guys,
I've got quite a specific data model that I'm trying to create in POCO classes, and I've tried so many different approaches and visited so many websites, but none seem to cover my specific example.
So I'm going to throw this out there and hope someone can help me.
I've got products, and products have ingredients. In our business though, these products can get supplied by different vendors, each with a slightly different set of ingredients.
So I've got my base classes:
public class Product
{
public int ProductID { get; set; }
public string ProductNumber { get; set; }
public string Description { get; set; }
public virtual ICollection<Supplier> Suppliers { get; set; }
}
public class Vendor
{
public int VendorID { get; set; }
public string Name { get; set; }
public virtual ICollection<Supplier> Suppliers { get; set; }
}
public class Ingredient
{
public int IngredientID { get; set; }
public string Name { get; set; }
}
public class Supplier
{
public int SupplierID { get; set; }
public virtual ICollection<Product> Products { get; set; }
public virtual ICollection<Vendor> Vendors { get; set; }
}
So as you can see, I've gotten as far as realising that my model needs a join entity, because my ingredients come from a specific vendor for a product, I need that join entity called supplier (I think).
This is where I am getting stuck... My product can be supplied by one or more vendors. Each vendor, per product, will have a list of ingredients for that product
I can't even get past whether I need the join entity or not.
What would be awesome was if I could navigate the data structure like this (or something similar):
articles[1].Vendors[3].Ingredients;
Yet again, I don't know enough about Entity Framework to know if this is possible.
The way I'm doing it now feels... wrong. What is the correct way to be doing what I want to do? Do I need the join table or can I somehow navigate the ingredients for a vendor for a product, maybe using the modelbuilder to make the product->vendor->ingredient link required?
Any help would be greatly appreciated!
Edit:
I also think I don't understand enough about Entity Framework's relationship ideas.
So one product can have multiple vendors, which is a 1-to-many relationship. But a Vendor can have many products. So is this actually a many-to-many?
I can definitely model what I want to do on a SQL Express server, but I really want to use Entity Framework. I just need a better understanding of my modelling requirements.

It is not problem of EF. It is common entity-relationship task. You must simply improve your model. You can for example divide Product into two entities: ProductDefinition, ProductInstance
public class ProductDefinition
{
public int ProductID { get; set; }
public string ProductNumber { get; set; }
public string Description { get; set; }
public virtual ICollection<ProductInstance> Instances { get; set; }
}
public class ProductInstance
{
public int ProductInstanceID { get; set; }
public virtual Supplier Supplier { get; set; }
public virtual ProductDefinition Definition { get; set; }
public vritual ICollection<Ingredient> Ingredients { get; set; }
}
In your system you will work with ProductDefinition instead of original Product. This definition is like group of same products and it points to all possible manufacturings of products provided by different suppliers with different ingredients.
I don't know your context but it seems strange to count products with different ingredients as "same".

Why do you use the code first, instead of making the design of the database first? Maybe this will be a bit easier. Additionally i cant see the relation between Vendor and Ingredient, or Product and Ingredient
For my understanding:
It's a new product, when a vendor uses other ingredients?

Related

How to make EF Core Power Tools generate one-to-one instead of one-to-many relationship

I have a less than ideal data model that is something like this (a country can have no or exactly one central bank):
Table COUNTRY:
COUNTRY_ID: Guid
Table CENTRAL_BANK:
CENTRAL_BANK_ID: Guid
COUNTRY_ID: Guid
Of course it would be better if we head a CENTRAL_BANK_ID in the COUNTRY table, then it would probably work as intended.
But now we get something like:
class Country
{
public Guid CountryId { get; set; }
public virtual ICollection<CentralBank> CentralBanks { get; set; }
}
But what we want is of course:
class Country
{
public Guid CountryId { get; set; }
public virtual CentralBank? CentralBank { get; set; }
}
Is there some way to tell the EF Core Power Tools that the relationship is really 1:0..1 and not 1:N?

Enity Framework Core: Handling large number of enum-like fields

I am currently facing the following problem:
I have a model class LargeDataClass with many fields (200+).
Many of these fields (~50-80) are enum-like (i.e. they can be filled out with certain sets of options in the UI).
Now my approach was to model these as enum classes, like
[Table("tbl_enum_one")]
class EnumOne {
public int ID { get; set; }
public string Name { get; set; }
}
[Table("tbl_large_dataclass")]
class LargeDataClass {
public EnumOne EnumOne { get; set; }
public int EnumOneId { get; set; }
//...
}
This has the major advantage of being easily extendable (to add a dropdown option in the UI, just add a row to the table).
Now I am facing some concerns/problems:
When I fetch my model class LargeDataClass from the DB with all its enum fields included, there will be a lot of joins (as I stated above, there are like 50 to 80 of these fields). I am worried that will have a big impact on query performance. Plus create/update/delete might be quite slow due to the large number of indexes to be updated.
MySQL won't even let me create a table tbl_large_dataclass with that many FKs (too many indexes on a single table).
So now I am considering two (in my view really unfortunate) options:
Using regular enums, so no enum classes with their own tables, storing them as simple int/string fields in the DB. This would cause no performance concerns at all, but unfortunately, the 'live' extendability is quite important, so this option would only be the last resort.
Using the Enum classes, but having just the ID of the enum in the LargeDataClass, so kind of keeping the fact that this is a foreign key secret from the DB. If I wanted to display a LargeDataClass object somewhere, I would have to separately fetch the enum classes. Plus I would have to make extra sure everywhere that I only use Ids that are really present in the enum table.
I am really unsure what would be the best approach here.
Database is not an object store and you have to design it accordingly. I have changed you schema and only two tables are needed for storing dropdown values.
[Table("tbl_enum_type")]
public class EnumType {
public int ID { get; set; } // PK
public string Name { get; set; }
}
// PK (EnumTypeId, Id) - reusing the same index for dropdown generation
[Table("tbl_enum_value")]
public class EnumValue {
public int ID { get; set; }
public string Name { get; set; }
public int Order { get; set; } // for dropdown ordering
public int EnumTypeId { get; set; }
public EnumType EnumType { get; set; }
}
// store only ID's, no FK
[Table("tbl_large_dataclass")]
public class LargeDataClass {
public int EnumOneId { get; set; } // EnumTypeId 1
public int EnumSecondId { get; set; } // EnumTypeId 2
//...
}
For generating dropdowns, you have to cache EnumType and EnumValue tables in memory in useful structure.
Override method SaveChanges/SaveChangesAsync and check saved Id's according to cached data.
It will not help if your database is changed via SQL, but here we have trade-off between performance and consistency. Probably good trigger may help here.
UPDATE:
Consider to restructure LargeDataClass to two tables
[Table("tbl_option_bag")]
public class OptionBag {
public int Id { get; set; }
public ICollection<Option> Options { get; set; }
}
[Table("tbl_options")]
public class Option {
public int Id { get; set; }
public int OptionBagId {get; set; }
public int EnumTypeId { get; set; }
public int EnumId { get; set; }
//...
}
Here you can use FK and DTO can be generated on selecting Options navigation property.

EF - Saving a new entity with linked existing entities creating duplicates

public class Car {
public string SomeProperty { get; set; }
public Manufacturer Manufacturer { get; set; }
public IList<Color> Colors { get; set; }
}
public class Manufacturer {
public int Id { get; set; }
public string Name { get; set; }
}
public class Color {
public int Id { get; set; }
public string Name { get; set; }
}
I already have tables full of Colors and Manufacturers. When I create a new car I want to be able to assign it a Color and Manufacturer bound from .net MVC.
When I save my new car with
context.Cars.Add(car);
A new Car is created (great) but a new Color and Manufacturer are also created even though these objects already had an Id and Name set that matched the content in the database.
The two solutions I see are to either write a custom save method for car, and tell the context that the Manufacturer and Color are Unchanged.
context.Cars.Add(car);
context.Entry(car.Manufacturer).State = EntityState.Unchanged;
foreach (Color color in car.Colors)
context.Entry(car.Color).State = EntityState.Unchanged;
Alternatively, to load the Manufacturer and Color from EF and then link them to the Car, instead of using the MVC bound objects.
car.Manufacturer = carRepository.GetManufacturer(car.Manufacturer.Id);
car.Colors = carRepository.GetColorsById(car.Colors);
I am not thrilled by either solution as this example is very trivial, but my real cases are far more complicated. I don't really want to have to fiddle around with EF in detail for each object I save. I have lots of complex object graphs to save and this seems very error prone.
Is there a way of making EF behave more like NHibernate, whereby you can give it something with an ID already assigned and it will assume without your intervention that it already exists?
Edit - question clarified to show collection of existing entities as well as many-to-one relationships.
Unfortunately, EF does not have anything like session.Load in NHibernate that allows you to get a proxy from an id.
The usual way to deal with this in EF is create a separate FK field containing the scalar value that corresponds to the reference. For example:
public virtual Manufacturer Manufacturer { get; set; }
public int ManufacturerId { get; set; }
Then you only have to set ManufacturerId and it will be saved correctly.
(So much for "POCO" and "code first". Pffffff)
You can define scalar properties in your entities and bind the values to them instead. Eg add
ManufacturerId and ColorId
public class Car {
public string SomeProperty { get; set; }
public int? ManufacturerId { get; set; }
public virtual Manufacturer Manufacturer { get; set; }
public int? ColorId { get; set; }
public virtual Color Color { get; set; }
}
Then set those scalar properties when you assign (eg through a DropDownList)
This way you can avoid loading many related entities to populate the entity.

Entity Framework 4.1 one to one relationship nullable

Hello everybody again,
I need some help in this logic for EF 4.1
I have one table with data for a customer. I have also another table with a survey i need to compile when needed.
So initally i could insert a new customer and after some days I'll fill the survey form. Then the relationship MUST be one-to-one and optional (just because this survey could never be compiled for a customer).
I digged in some examples online but i'm really stuck.
Thank you in advance.
Simply define your entities like:
public class Customer
{
public int Id { get; set; }
...
public virtual Survey Survey { get; set; }
}
public class Survey
{
[Key, ForeignKey("Customer")]
public int Id { get; set; }
public virtual Customer Customer { get; set; }
}
If you don't like data annotations remove them and place this into OnModelCreating in your context:
modelBuilder.Entity<Customer>()
.HasOptional(c => c.Survey)
.WithRequired(s => s.Customer);

Applying Domain Model on top of Linq2Sql entities

I am trying to practice the model first approach and I am putting together a domain model. My requirement is pretty simple: UserSession can have multiple ShoppingCartItems.
I should start off by saying that I am going to apply the domain model interfaces to Linq2Sql generated entities (using partial classes). My requirement translates into three database tables (UserSession, Product, ShoppingCartItem where ProductId and UserSessionId are foreign keys in the ShoppingCartItem table). Linq2Sql generates these entities for me. I know I shouldn't even be dealing with the database at this point but I think it is important to mention.
The aggregate root is UserSession as a ShoppingCartItem can not exist without a UserSession but I am unclear on the rest. What about Product? It is defiently an entity but should it be associated to ShoppingCartItem?
Here are a few suggestion (they might all be incorrect implementations):
public interface IUserSession {
public Guid Id { get; set; }
public IList<IShoppingCartItem> ShoppingCartItems{ get; set; }
}
public interface IShoppingCartItem {
public Guid UserSessionId { get; set; }
public int ProductId { get; set; }
}
Another one would be:
public interface IUserSession {
public Guid Id { get; set; }
public IList<IShoppingCartItem> ShoppingCartItems{ get; set; }
}
public interface IShoppingCartItem {
public Guid UserSessionId { get; set; }
public IProduct Product { get; set; }
}
A third one is:
public interface IUserSession {
public Guid Id { get; set; }
public IList<IShoppingCartItemColletion> ShoppingCartItems{ get; set; }
}
public interface IShoppingCartItemColletion {
public IUserSession UserSession { get; set; }
public IProduct Product { get; set; }
}
public interface IProduct {
public int ProductId { get; set; }
}
I have a feeling my mind is too tightly coupled with database models and tables which is making this hard to grasp. Anyone care to decouple?
Looks like you are on the right track. Half of the whole "doing DDD right" is having the right base classes. Have a look at this great DDD applied to C# resource:
http://dddpds.codeplex.com/
The source code is available and is very readable.
So, with regards to having ID in the model. The ID is a database thing and the usual approach is to keep all persistence out of the Model and restrict the model to the business logic. However, one normally makes an exception for the identifier and buries it in the Model base class like so:
public class ModelBase {
protected readonly object m_Key;
public ModelBase(object key) {
m_Key = key;
}
}
This key is used by your persistence layer to talk to the database and is opaque. It's considered quite OK to downcast the key to the required type, because you know what it is.
Also, the Domain Objects are pretty much on the bottom of your architecture stack (just above the Infrastructure layer). This means that you can make them concrete classes. You will not have multiple implementations of the domain models, so the interfaces are unnecessary, which is what Domain Driven Design is about - Domain first.
public Class UserSession : ModelBase {
public UserSession(Guid Id):base(Id) {}
public Guid Id { get{ return m_Key as Guid;} }
public IList<ShoppingCartItem> ShoppingCartItems{ get; set; }
}
public class ShoppingCartItem : ModelBase {
public ShoppingCartItem ():base(null) {}
public UserSession UserSession { get; set; }
public Product Product { get; set; }
}
Typical shopping cart or customer-order examples prefer making UserSession (or Order) the root of aggregate. Individual items should be children of this session/order. It is up you whether individual items in the cart should have a meaningful id. I would prefer no, since 5 widgets in the cart are indistinguishable from another 5 widgets. Hence, I would model cart items as a collection of value objects.
Common problem with shopping cart items is whether they should include price, or not. if you include price, you will have your cart independent from changes of product price. It is very desirable if you want to store you cart for historical reasons since it is valuable to know how much items in the cart cost according to price when they were bought, not according to current.
Product should form definitively an aggregate by itself. Period.
Now, I don't know if all of this is easily implementable in LINQ to SQL, but you can try.