EF Many to many relationship with additional properties - c#

I know that this is a repeated question and I know that this is not possible if there are additional properties in the "in the middle" table.
I had an idea how to get the effect of an m:N relationship instead of an 1:n-n-1, but I'd like to hear some other thoughts.
If I have three entities, A, B, and AB where AB makes the A:B relation possible and it has additional properties.
Using Databasefirst approach, I thought to make a partial class of A and B.
public partial Class A
{
public IEnumerable<EntityObject> Bs
{
get
{
return this.Select(p=>p.AB.B);
}
set { //... }
}
}
Could something like this be possible.
Just doodling in my head. I am currently on vacation and have no computer, so this is not tested but just written on my cell phone.
I see that this could be a problem after context disposing or detaching, also with including in an eager loading approach.
Any thoughts?

If you are already treating AB as a distinct entity, then to get all B from A all you need is something like this:
public partial class A
{
public IQueryable<B> Bs {
get { return this.ABs.AsQueryable().Select(ab => ab.B).Distinct(); }
}
}
I'm not sure how well this will perform, as compared to a built-in Many-To-Many supported by EF (without any payload), but it will give you what you are asking.

If technically possible or not, expressing such a relationship with "additional properties in the in the middle table" as many-to-many relationship is just wrong because it hides that the "middle table" has a business meaning and therefore must be an entity on its own.
A somewhat classical example for such a model are RawMaterial and Product: A RawMaterial can be used in multiple Products and a Product can be made of multiple RawMaterials. The entity in between - maybe called RecipePart - contains a Quantity how many pieces of a given RawMaterial are used in a given Product.
If you have for example the product ChocolateBar and work with its relation to raw materials you will deal with a recipe that says a ChocolateBar has 60 units of Chocolate and 40 units of Milk, i.e. ChocolateBar has a collection of RecipeParts and every RecipePart describes the quantity and refers to the related RawMaterial. A ChocolateBar does not have a direct collection of RawMaterials in this business model.
For a particular query (maybe some statistics) you might be only interested in its raw materials - a chocolate bar is made if chocolate and milk, no matter how many units - but that is a special query in your business model and kind of an aggregation that ignores some pieces of the full detailed model information. This is what your helper property this.Select(p=>p.AB.B); does: It does not express the full relationship but is a specialized query that says: Give me only the RawMaterials for this Product, I don't want to know each quantity.
Characteristically you have left the property setter set { //... } a stub. When adding or changing entities it becomes obvious that the relationship cannot be many-to-many. It is not possible to assign only a list of RawMaterials to a Product. You must add the information how many units of each RawMaterial to get a valid Product model which means that Product must be related to the "middle entity" RecipePart.

Related

Can't get Entity Framework zero-or-one to zero-or-one relations to work

I am fairly new to Entity Framework and I've spend the last 4 hours day trying to make a (to me seemingly) simple relation to work.
I have three classes: Person, House, Garage
A Person can have none or multiple (0..n) Houses and Garages
A House can have (0..1) a Garage and must only belong to one Person
A Garage can therefore also have (0..1) a House and must only belong to one Person
There are Garages with no Houses and Houses with no Garages
At no point can there be a Garage or House without a related Person (that's easy, I think I got that one)
By now I've read like 10 different documentations that all make matters only more confusing to me. I've tried solving it using annotations but it seems that I also have to incorporate this fluent api stuff.
The furthest I've gotten is to have Houses and Garages associated to a Person, but not to eachother. Now I've been stuck having to define the principle end of a relationship, which I don't even understand what it's supposed to do.
Right now I get
Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.
from trying to solve a barrage of different errors.
I've been implementing relations like these for ages in PHP, manually, by writing all my own queries. This is the first time I'm doing database stuff in a bigger framework that is supposed to make things easier, but so far I'd have had more success in filling the objects myself from a DatabaseReader.
From my logical point of view the relation between the three Classes/Tables should be really simple yet I can't find examples of similar relations or get it to just work.
So my questions are:
Is a zero-or-one to zero-or-one relation really that special that it's so hard to design in code first?
Wouldn't the Garage table not need a foreign key to the House table, because it's possible to find the corresponding House by filtering the House table for the GarageId? I've tried implementing a lookup of the House in code but noticed that it breaks a lot of LINQ because it's not a database property anymore.
I've tried Annotations and Fluent API, all the top examples of 0..1 to 0..1 (or really, any other) relations from SO, tried swapping fields in the modelBuilder because it didn't look right in the DB... until reaching a point where the DB looks kinda okay but the code still refuses to work. What else can I try?
Maybe I've been doing all the DB stuff wrong all my life and having relations like these are unhealthy for everyone involved. In that case, what alternatives would you suggest that I try to replicate the Person-House-Garage relation described above? Are there any helper classes that I can shove between Houses and Garages to get it to work?
I have prepared a demo project: https://github.com/Zaphox/DatabaseTest
It contains the three classes, context and a test of the relations that should successfully complete (but doesn't).
Thanks for any and all help and hints that lead to the solution to this problem!
Z.
Update 1
After trying all suggestions I noticed that any circular relation causes errors in EF and it seems that it's just not possible to create references like these, even 0..1 to 0..1 self references do not seem to work.
I have however implemented a hack that at least lets me work, but it's horrible and I would love for someone to tell me that there's a better way.
Only Houses have references to Garages, not vice versa
To get a House belonging to a Garage a separate query is required (db.Buildings.OfType<House>().Where(h => h.Garage.Id == myGarage.Id).First())
Also I thought about adding a NotMapped House field in the Garage which does a lookup for the house by itself but it interferes with other queries, so that's not a valid option either.
What annoys me most is that there are a lot of articles about self-references and the likes, but only for 1 to n relations. Any attempt to make a 1 to 1, or 0..1 to 0..1 relation, always fails with EF not being able to figure out the "order".
The thing is that EF thinks it has to make put a GarageId FK in the House table, and a HouseId FK in the Garage table, which is nonsense. I only need a GarageId in the House table, which can used as a backwards reference. I can implement this code-wise but it's not compatible to EF queries and usually interferes with other running queries because you can't run two queries at once.
The demo project on GitHub is updated with the current version, which kinda works but requires a lot of additional querying every time I want to find the House belonging to a Garage.
Apparently the problem is the relationship between House and Garage.
I only need a GarageId in the House table, which can used as a backwards reference.
In fact this is what happens by default when you put only Garage navigation property on House class (with the only difference that the FK column name is Garage_Id). This is called unidirectional association. With such setup EF considers House to be the dependent, Garage to be the principal and both to be optional. The important part is that FK column is created in dependent table.
So this is what you have currently in your test project and it works. The problem arises when you make the association bidirectional by adding House property to Garage class:
public class House
{
public int Id { get; set; }
public Garage Garage { get; set; }
public string Style { get; set; }
}
public class Garage
{
public int Id { get; set; }
public House House { get; set; }
public int Size { get; set; }
}
In this case EF has no enough information what is your intention, so you need to provide that information. Remember that you want GarageId FK in House table, so here is the corresponding Fluent configuration that you need to add (the Map part is optional - if you skip it, the FK column will be called Garage_Id as in your current project):
modelBuilder.Entity<House>()
.HasOptional(e => e.Garage)
.WithOptionalDependent(e => e.House)
.Map(m => m.MapKey("GarageId"));
and everything will work as expected.
Entity Framework doesn't like relationships where there is no clear principal. In this case as either entity can exist without the other, neither is dependent on the other.
I would suggest redesigning your model so that you have an abstract class Building which House and Garage both derive from. You can then link your Person to a Building, and in the concrete types 'House' and Garage have a link to a Building for the association.
Example:
public abstract class Building
{
public Person Person { Get; Set; }
public Building Building { Get; Set; }
}
public class House : Building
{
// Other House properties
}
public class Garage : Building
{
// Other Garage properties
}
Then in the business logic you can enforce that the associated building cannot be of the same type with something like this.GetType() != Building.GetType()

My EF6 code-first model seems very inefficient but I don't know how to improve it. Am I missing something?

I am currently modeling two classes in asp.net using entity framework 6 using a code-first approach. A user can create a Widget, where a Widget has a collection of up to five WidgetOptions. The options are primarily strings but there is metadata associated with them that other users need to interact with (hence why they are not just a collection of strings). Currently, my classes look something like this:
Widget {
public string Name {g;s;}
... //more fields
public string ICollection<WidgetOption> Options {g;s;}
}
WidgetOption {
public string Option {g;s;}
... //more fields
}
This obviously seems simple enough, but here are my thoughts. So currently, when a user creates a widget in the UI, the controller is creating a list of WidgetOptions from the strings that the user enters, assigning the WidgetOptions to the Widget, then saving to the database context. As in:
Create(WidgetFormViewModel vm) {
var options = vm.WidgetOptions.Select(wo => new WidgetOption(wo)).ToList();
var widget= new Widget {
Options = options,
// ... other fields from vm
};
_context.Widgets.Add(widget);
_context.SaveChanges();
return RedirectToAction("Index", "Home");
}
The problem is that many users are likely to repeat WidgetOption strings. For instance, a lot of users might name an option "red" to make the Widget red (while that may seem weird, the actual case makes a lot more sense). Entity Framework is currently modeling the classes as having no columns in Widget that reference WidgetOption, and the WidgetOption having Id, Option, and Widget_Id columns.
So if two users make a Widget with a "red" WidgetOption, there will be two rows created in the WidgetOption table. Each with a unique Id and each with the corresponding Widget_Id. This seems really inefficient in terms of space, but I could be wrong (in that it's not as much space as I think is being wasted). I guess, even if I had a separate table with only unique WidgetOption strings, I would still need a relationship table that connected a Widget to Many WidgetOptions. That could be more efficient than the current model if the WidgetOption_Id in the relationship table was much smaller than the string itself, right? Except then I would have to search for each string that the user enters to see if it had been created. So I'd be sacrificing speed for storage.
I'm not sure if I'm overthinking this or not, but I just feel weird having a table where WidgetOptions are repeated. If there were only a few unique option strings, a many to many relationship makes more sense, while the current model would make sense if every option string was unique. It seems like the right choice can only be solved by figuring out which end of this scale occurs in practice.
Rewrite your Widget and WidgetOption entities this way:
Widget {
public string Name {g;s;}
... //more fields
public virtual ICollection<WidgetOption> WidgetOptions {g;s;}
}
WidgetOption {
public string Option {g;s;}
... //more fields
public virtual ICollection<Widget> Widgets {g;s;}
}
It would tell Entity Framework to define many-to-many relationship between Widget and WidgetOption. I believe this should take care of the issue that has been bothering you. Though, it might require a little change in code to add WidgetOptions.

DDD with EF: Collection of Value objects

In a studied domain, a Car may have many Tires and according to DDD concepts, Car is an aggregate root while Tire is a Value Object. '
Consider the following model:
class Car
{
public int Id {get;set;}
public virtual ICollection<Tire> Tires {get;set;}
}
[ComplexType]
class Tier
{
public string Manufacturer {get;set;}
public int Diameter {get;set;}
}
I'm afraid EF 6.0 + cannot implement this model. Am I right? Any way to implement Collection of Value Objects?
Complex types, according to MSDN documentation, cannot participate in associations and cannot contain navigation properties so this is not the right way.
With EF the only way is to have 2 tables (with an Id on the Tires table). You can also hide Id of the Tires table, you can insert a unique index on the foreign key of the Cars table but when you check if two tires are equal you need to check if both properties are equal.
This is a common issue with normalized persistence (which includes the SQL server which you access through EF). Entity framework makes it more difficult by not allowing you to have a protected key though.
One way is to have a Tires table which has an id, which will form part of a foreign key relationship with car. However, the idea of a unique key violates the fact that value objects should not rely on id's and should be compared by value. Being diligent, overriding Equals, and only comparing by value will allow you work with this solution; it does not matter if the actual objects are different as long as the equality comparison would return true if two tires matched. It is not pretty, I agree, but with EF it seems to be the only solution. If I am wrong, please someone correct me. If you go this route, remember to map your domain data to your DTO's in a way that the Id is removed. That way you keep the fact that tires have an id isolated.
Another solution is to serialize the tires using Json before being sent to SQL db (and then deserialize it back on read), but that is not something that I would personally suggest if you need to query on information in the tires (for example give me all cars that use this kind of tyre).
PS: Vaughn Vernon discusses this particular issue with Hibernate for Java in this book: http://www.amazon.co.uk/Implementing-Domain-Driven-Design-Vaughn-Vernon/dp/0321834577 Please read, it will solve a lot of the issues or questions you may have on the subject.

Entity Framework - Manually Add Properties to Many-to-Many Relationship - Model First

Scenario:
As mentioned here, once you add additional properties to a simple join table (many-to-many relationship), it's no longer a hidden association. These questions also address this:
Many-to-Many relationship in Entity Framework with relationship informantion
How can I add properties to an association (relationship) using the Entity Framework
The existing code already uses the simple, automatically hidden navigation properties, and there are some minor customizations to the autogenerated tables, and so I'd like to avoid refactoring the entire project when I alter the underlying relationship table.
Question:
Is there a way so that both the automatic navigation (many-to-many) accessors can remain, but I can also access the relationship entity directly?
I could just write my own accessors selecting from the relationship table, but then they're no longer EntityCollections and thus I'm concerned that I lose whatever magic happens under the hood like tracking, etc.
Can I manually add EntityCollections to entities?
Expectation:
Originally: Product* <-> *Offer
a Product has many Offers (like 50% off, BOGO)
the same Offer could apply to many Products ("Red Shirt" and "Blue Pants" are BOGO)
Desired: Product* <-[sort]-> *Offer
When I list Offers for a Product, I can sort them independently
i.e. "Red Shirt" has "50% off" then "BOGO", but "Blue Pants" shows "BOGO" then "50% off"
then I would want to be able to do:
// original access, do stuff
List<Offer> applicableOffers = currentProduct.Offers.Where(...);
// hit up the join table directly for properties
var applicableOffersInOrder = applicableOffers.OrderBy(o => o.ProductOffers.Sort);
rather than
var applicableOffersInOrder = currentProduct.ProductOffers
.OrderBy(o => o.Sort)
.Offers.Where(...);
I think the easiest way to do it is simply add two properties manually in a non-autogenerated partial class:
partial class Offer
{
public IQueryable<Product> Products
{
get { return this.ProductOffers.Select(x => x.Product); }
}
}
partial class Product
{
public IQueryable<Offer> Offers
{
get { return this.ProductOffers.OrderBy(x => x.Sort).Select(x => x.Offer); }
}
}
This won't help for when you want to add a new ProductOffer, but since you actually have extra data (Sort) you should be doing that via the ProductOffers collection anyway.

Create an IEnumerable<> of unrelated EF4 entities?

So here's the scenario...My website displays three different types of products for sale. For this example, we'll call them Furniture, Clothes and Toys. The three products are distinctly different, yet all share some of the same properties (Price, DateCreated, Description, ShortDescription, etc).
I'm using EF4 so when I display a list of products on the "Furniture" page, I can just do something like this:
IEnumerable<Furniture> furniture = Repository.GetFurniture();
Everything is working great when I'm only displaying one product type on a page. I have the entire set of Furniture displayed in a paged list of 10 products per page, sortable by Name, Price, Ad Date, etc.
However, I want to create a link to "See all of this seller's products". This page will need to display EVERYTHING the seller has listed regardless of whether it's furniture, clothes or toys. The trick is, it also needs to be sortable by the fields that all of the products share in common (price, name, ad date, etc).
So what I'd like to do is something like this pseudo-code:
IEnumerable<IProduct> products =
Repository.GetAllProducts(sellerId).OrderBy(sortField).Skip(skip).Take(take);
Then, in the View (yes, I'm referencing EF4 entities in the view):
#foreach (var product in Model.products.OrderBy(sortField))
{
if (product is Furniture)
{
Html.RenderPartial("FurnitureResult", (Furniture)product)
}
else if (product is Clothes)
{
Html.RenderPartial("ClothesResult", (Clothes)product)
}
// repeat for toys
}
So my question is:
What would the call to Repository.GetAllProducts(sellerId) look like? It would need to be a set of LINQ queries that gets all of the furniture, clothes and toys and combines them into a single IEnumerable<>. If that's not possible, what other methods could I employ to get a list of products like this?
Essentially I just need to know what the best method is to group a set of unrelated objects with some common properties into a single collection and then sort that list by one of the common properties and then determine which concrete class it is as I iterate over them.
Thanks!
Try to create the partial classes for each of the entities. The partial class will implement the interface IProduct, which will not be a part of EF, but will have the properties to return price, name, ad date etc.
Implement these properties getters to return specific properties of the entity and implement the method GetAllProducts to return the combined enumerable of all entity types.
Have you considered creating a Database View, and adding it to your Context. Otherwise,
Repository.GetAllProducts(sellerId).OrderBy(sortField).Skip(skip).Take(take);
would run in the memory, I mean you'd have to at least execute it on 3 different DbSets, and merge those to get the final set.
Another option is using the Inheritance, make a base Product type, and inherit the 3 different products from it. http://weblogs.asp.net/manavi/archive/2010/12/24/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-1-table-per-hierarchy-tph.aspx
Check out all Inheritance options from the above web blog, so there are 2 more blogs to follow up in it.
Alternative options:
Union all products in a collecton of type Object so Repository.GetFurniture().Cast().Union(Repository.GetClothes().Cast());
Or: use dynamic and ducktyping and render using common property names. (bleg)

Categories

Resources