Many-to-Many relationship in WPF with EF and SQLite - c#

I'm following this tutorial in order to implement a local database (using SQLite) for my WPF application.
Everything works as it should, although I have a logical problem I don't know how to solve using this approach. I'll simplify it below.
Database-wise, I have 2 tables (A,B) which share a many-to-many relationship, and thus require a JOIN table as well (ABJoin).
In my actual code however, I'd like to use only 2 models: A and B, which each having a list of the other type. Like this:
public class A {
// ...fields
List<B> bList;
}
public class B {
// ...fields
List<A> aList;
}
How can it be implemented in EF+SQLite?
Searching online, I have found some solutions, but nothing that applies to SQLite, so I was not sure how they'd work.

Configure Many-to-Many Relationships in Code-First

If you are using many to many join table then your each class should have a list of the join table.
It cannot work the way you are thinking.

Are you sure that it is a good idea not to have a third entity?
Lets say your two entities were DepartmentStore and Product, a typical example for an n:n relationship. A department store can sell many products and a product may be available in many departments stores. This results in a third entity which connects the two above, in the example above this would something like ProductAvailability.
If you think about it more careful, then you might realize that the new connecting entity might have properties of its own. In my example this might be NumberOfProducts, will states the available quantity of a product in a certain department store.
In my experience, it is quite common for the connecting entity to have a real value that goes beyond just connecting two other entities.
I also took a look at you example which it about Album and Artist entities.
Do you want to make a data model where an Album can be created by more than one Artist?

Entity Framework doesn't have automatic many-to-many mapping.
Instead of this, you can map A and B to intermediate table as one-to-many.
If you are not obliged to use only EF, I suggest to try NHibernate ORM instead.
It has convenient many-to-many mapping and generally more powerful.

Related

EntityFramework update Many to Many without materializing the collection

I have linked 2 Entities Project, Train with a many to many relationship.
Now, I want to add an existing Train to Project.Trains. Unfortunately, the collection has a lot of entries. When I write project.Trains.Add(...) it materializes the complete collection and this takes a lot of time. Is there a way to tell EF that I want to add the Train to the Project without materializing the collection, so that the action is faster?
Assuming that the Train.Projects collection will have fewer items than its Project.Trains counterpart, a quick solution would be adding the Project to the Train instead of the other way around:
train.Projects.Add(project);
Yes, there is a way of doing it, but it is very difficult, and it involves digging into the very interna of EF. If you use the proxies generated by EF, they will typically implement IEntityWithRelationships. You will be able to access the relationship manager as
var entityWithRelationships = project as IEntityWithRelationships;
var relatedEnd = entityWithRelationships.RelationshipManager.GetRelatedEnd("RelationshipName as found in Metadata");
With this setup you should be able to add the Train as follows:
context.Trains.Add(train);
relatedEnd.Add(train);
As said, it is a long way to make it work, but it would solve your problem.
You can:
1- Turn off lazy loading. This way, accessing Trains doesn't make a
query to DB. But be aware of the side effects because you may have
already relied on it.
2- Close to Relational Database world: Add FK property to Train entity and set it on the newly created
object:
newTrain.ProjectId = project.Id
3- Even more Close to Relational Database world: Add junction table of ProjectTrain, to implement Many-to-Many relation
public class ProjectTrain
{
public int Id {get;set;}
public Project Project {get;set;}
public Train Train {get;set;}
}
and add this to the context:
DBSet<ProjectTrain> ProjectTrains {get;set;}
and finally, make a new entity of ProjectTrains and set the Train and Project properties

How to update the cross reference table?

In EF cross reference tables are abstracted away by creating many-to-many relationships. E.G.
There's a SQL table dbo.TrialContactCrossReference that relates TrialContactId to TrialID. Now, EF did not generate an Entity TrialContactCrossReference because it went with this MANY-MANY relationship thing. How do I add a new row to said table?
I tried
context.TrialContacts.??? and context.ClinicalTrials.??? and just don't know what to do with this. If I have a new Contact that I want to relate to a trial how am I supposed to go about it?
trial.Contacts.Add(contact);
OR
contact.Trials.Add(trial);
OR (and my advise)
you could create an additional entity for cross reference table. this will convert many-many to 2 one-many relationships. more then %90 cases crosstables has additional columns (at least IsActive, RecordDate etc.) even it doesnt, it may be so in future and it requires you make lots of changes in code.
If I have a new Contact that I want to relate to a trial how am I supposed to go about it?
Assuming you have an existing Contact instance just do:
trial.TrialContacts.Add(contact);
context.SaveChanges();
EF will take care of the intermediate table insert for you. Note that adding Contacts and Trials works the same as if they weren't related.
There's a trick that was not obvious to me when setting this up. TableA must be added to TableB, not just to itself. In fact, looking at the generated entities each entity has a List<> of the other entity.
class TableA
{
List<TableB> TableB;
}
class TableB
{
List<TableA> TableA;
}
For example, if I want to add a TrialContact to a ClinicalTrial then I write:
context.ClinicalTrials.TrialContacts.Add(trialContact);
context.SaveChanges()
Then the xRef table be updated to reflect the relationship.

EF DB-first mapping mess

I have a frustrating situation owing to this little quirk of EF. Here's a simple demo of the behavior. First the DB schema:
As you see, RestrictedProduct is a special case of product, which I'm intending to make a subclass of Product with some special code.
Now I import to an EF data model:
Oops! EF saw that RestrictedProduct had only 2 fields, both FKs, so it mapped it as a one-to-many relationship between Product and Restriction. So I go back to the database and add a Dummy field to RestrictedProduct, and now my EF model looks much better:
But that Dummy field is silly and pointless. Maybe I could delete it? I blow away the field from the DB table and the entity model, then refresh the model from the DB...
Oh, no! The Product-Restriction association is back, under a new name (RestrictedProduct1)! Plus, it won't compile:
Error 3034: Problem in mapping fragments starting at lines (x, y) :Two entities with possibly different keys are mapped to the same row. Ensure these two mapping fragments map both ends of the AssociationSet to the corresponding columns.
Is there any way to prevent this behavior, short of keeping the Dummy field on the RestrictedProduct table?
I just came across the same issue, and as an alternative to putting the dummy field in your RestrictedProduct table to force the creation of an entity you can also make your RestrictedProduct.RestrictionId field nullable and EF will then generate an entity for it. You can then modify it to use inheritance and any subsequent "Update model from database" will not cause undesired nav properties. Not really a nice solution but a work around.
Let's walk slowly into your problem.
1st thing you need to decide is if the restricted product is
really a special case of product or is it a possible extension
to each product.
From your original DB Scheme it seems that any product may have
a relation to a single restriction however a single restriction
can be shared among many products.. so this is a simple 1 to many
situation which means that restricted product is NOT a special case
of product! Restriction is an independent entity which has nothing
to do with product in a specific way.
Therefore EF is correct in the 1st importation of your scheme:
1. a product can have 0 or 1 restrictions.
2. a restriction is another entity which can be related to many products.
I do not see your problem.

Many to Many (self related) specific order entity framework

Hello I'm trying to do the impossible apparently.
I need a self referenced table with a many to many relationship to itself that also has a specific order in c# entity framework (4.2) database first.
Think of it like Friends having Friends in which they order their friendship > Best Friend to Worst Friend.
Is there anyway to do this without using the "FriendToFriend" relationship entity? I would like to be able to use Friend.Friends (removing the order column creates it), but I would have a default order based on their friendshipOrder. My work around is looking like extending the generated classes to have a new property for Friends in order.
Any one else have any better ideas?
Entity framework does not support ordered collections. This is one of many situations where EF shows its immaturity.
Try nHibernate if it is a viable option. It supports ordered collections.
With EF you will have to map the intermediate table with extra column and manually adjust the ordering according to your logic.
I know I'm late to this, but when designing this as a data model, I would prefer to add a relationship table, and that relationship table should have a property that defines the order (for example, worst friend is 0, best is 100).
Then, in EF, I would explicitly order by that property, if the list I'm retrieving should be of that order.
That means that whatever method you use to query the data, that relationship can be consistently used. So if you were using EF, you could use it (although, yes, it's not as handy as Friend.Friends, but the code would be clearer as to its intention - Friend.FriendRelationships.Select(p => p.Friend).OrderBy(p => p.OrderValue)), and if you were using direct SQL, then you could use it too.
If I came across Friend.Friends in code, I would have no idea what ordering would be applied to it.
If you must have it though, you could always add it as a non-db property -
public class Friend
{
public virtual List<FriendRelationship> UnorderedFriendList { get; set; }
[NotMapped]
public IEnumerable<Friend> Friends
{
get
{
return UnorderedFriendList.Select(p => p.Friend).OrderByDescending(p => p.OrderValue);
}
}
}

Fluent nHibernate Join

I have an entity that maps to a table called Rule. The table for this entity has an FK to another Table called Category. I'm trying to figure out how to pull in a property from Category in my Rule entity. I'm pretty sure I want to use a join in my entity mapping, but I can't figure out how to configure it so that it works. Here is my mapping:
Join("Category", x =>
{
x.Map(i => i.CategoryName, "Name");
x.KeyColumn("CategoryId");
x.Inverse();
});
Here is the SQL that it's generating...
SELECT ...
FROM Rule rules0_ left outer join Category rules0_1_ on rules0_.Id=rules0_1_.CategoryId
WHERE ...
Here is the SQL that I want.
SELECT ...
FROM Rule rules0_ left outer join Category rules0_1_ on rules0_.CategoryId=rules0_1_.Id
WHERE ...
I can't seem to find anything on the JoinPart that will let me do this. Subselect looks promising from the little bit of documentation I've found, but I can't find any examples of how to use it. Any advice on this problem would be much appreciated. Thanks!
"Join" is poorly named. a "join" in an NHibernate mapping implies a zero-to-one relationship based on a relation of the primary keys of the two tables. You would use a join if, for instance, you had a User table and a UserAdditionalInfo table, with zero or one record per User. The UserAdditionalInfo table would likely reference the PK from User as both a foreign key and its own primary key. This type of thing is common when a DBA has to religiously maintain a schema for a legacy app, but a newer app needs new fields for the same conceptual record.
What you actually need in your situation is a References relationship, where a record has a foreign key relationship to zero or one other records. You'd set it up fluently like so:
References(x=>Category)
.Column("CategoryId")
.Inverse()
.Cascade.None();
The problem with this is that Category must now be mapped; it is a separate entity which is now related to yours. Your options are to live with this model, to "flatten" it by making the entity reference private, changing the mapping to access the entity as such, and coding "pass-throughs" to the properties you want public, or by using a code tool like AutoMapper to project this deep domain model into a flat DTO at runtime for general use. They all have pros and cons.

Categories

Resources