Entity Framework STEs and many-To-many associations - entity-framework

I'm fairly new to EF and STE's, but I've stumbled on a painful point recently, and I'm wondering how others are dealing with it...
For example, suppose I have two STE's: Employee and Project. It's a many-to-many relationship. Each entity has a navigation property to the other (i.e. Employee.Projects and Project.Employees).
In my UI, a user can create/edit an Employee and associate it with multiple Projects. When the user is ready to commit, a list of Employees is passed to the server to save. However, if an Employee is not added to the "save list" (i.e. it was discarded), but an association was made to one or more Projects, the ApplyChanges extension method is able to "resurrect" the Employee object because it was "connected" to the object graph via the association to a Project.
My "save" code looks something like this:
public void UpdateEmployees(IEnumerable<Entities.Employee> employees)
{
using (var context = new EmployeeModelContainer(_connectionString))
{
foreach (var employee in employees)
{
context.Employees.ApplyChanges(employee);
}
context.SaveChanges();
}
}
I've been able to avoid this issue to now on other object graphs by using FKs to manipulate associations as described here: http://blogs.msdn.com/b/diego/archive/2010/10/06/self-tracking-entities-applychanges-and-duplicate-entities.aspx
How does one handle this when a many-to-many association and navigation properties are involved?
Thanks.

While this answer's a year late, perhaps it will be of some help to you (or at least someone else)
The simple answer is this: do not allow Entity Framework to infer m:m relationships. Unfortunately, I'm not aware of a way of preventing this, only how to deal with it after the fact.
By default, if I have a schema like this:
Employee EmployeeProject Project
----------- --------------- ----------
EmployeeId ---> EmployeeId |--> ProjectId
Name ProjectId ----- Name
... ...
Entity Framework will see that my EmployeeProject table is a simple association table with no additional information (for example, I might add a Date field to indicate when they joined a project). In such cases, it maps the relationship over an association rather than an entity. This makes for pretty code, as it helps to mitigate the oft-referenced impedence mismatch between a RDBMS and object-oriented development. After all, if I were just modeling these as objects, I'd code it the same way, right?
As you've seen, however, this can cause problems (even without using STE's, which cause even MORE problems with m:m relationships). So, what's a dev to do?
(The following assumes a DATABASE FIRST approach. Anything else and you're on your own)
You have two choices:
Add another column to your association table so that EF thinks it has more meaning and can't map it to an association. This is, of course, bad design, as you presumably don't need that column (otherwise you'd already have it) and you're only adding it because of the particular peculiarities of the ORM you've chosen. So don't.
After your context has been generated, map the association table yourself to an entity that you create by hand. To do that, follow the following steps:
Select the association in the designer and delete it. The designer will inform you that the table in question is no longer mapped and will ask you if you want to remove it from the model. Answer NO
Create a new entity (don't have it create a key property) and map it to your association table in the Mapping Details window
Right-click on your new entity and add an association
Correct the entity and multiplicity values (left side should have your association entity with a multiplicity of *, right should have the other entity with a multiplicity of 1)
Check the option that says "Add foreign key properties to the Entity"
Repeat for the other entity in the association
Fix the property names on the association entity (if desired...not strictly necessary but they're almost certainly wrong) and map them to the appropriate columns in the Mapping Details window
Select all of the scalar properties on your association entity and set them as EntityKey=True in the Properties window
Done!

Related

Entity Framework: Doing JOINs without having to creating Entities

Just starting out with Entity Framework (Code First) and I have to say I am having a lot of problems with it when loading SQL data that is fairly complex. For example, let's say I have the following tables which stores which animals belongs to which regions in the world and the animal are also categorized.
Table: Region
Id: integer
Name string
Table AnimalCategory
Id integer
Name: string
RegionId: integer -- Refers back Region
Table Animal
Id integer
AnimalCategoryId integer -- Refers back AnimalCategory
Let's say I want to create a query with Entity Framework that would load all Animals for a specific region. The easiest thing to do is to create 3 Entities Region, AnimalCategory, and Animal and use LINQ to load the data.
But let's say I am not interested in loading any AnimalCategory information and define an Entity class just to represent AnimalCategory so that I can do the JOIN. How can I do this with Entity Framework? Even with many of its Mapping functions I still don't think this is possible.
In non Entity Framework solutions this is easy to accomplish by using INNER JOINs in SPs or inline SQL. So what are my options in Entity Framework? Shall I pollute my data model with these useless tables just so I can do a JOIN?
It's a matter of choice I guess. EF choose to support many-to-many associations with transparent junction tables, i.e. where junction tables only have two foreign keys to the associated entities. They simply didn't choose to support this far less common "skipping one-to-many-to-many" scenario in a similar manner.
And I can imagine why.
To start with, in a many-to-many association, the junction table is nothing but that: a junction, an association. However, in a chain of one-to-many (or many-to-one) associations it would be exceptional for any of the involved tables to be just an association. In your example...
Animal → AnimalCategory → Region
...AnimalCategory would only have a primary key (Id) and a foreign key (RegionId). That would be useless though: Animal might just as well have a RegionId itself. There's no reason to support a data model that doesn't make sense.
What you're after though, is a model in which the table in the middle does carry information (AnimalCategory.Name), but where you'd like to map it as a transparent junction table, because a particular class model doesn't need this information.
Your focus seems to be on reading data. But EF has to support all CRUD actions. The problem here would be: how to deal with inserts? Suppose Name is a required field. There would be no way to supply its value.
Another problem would be that a statement like...
region.Animals.Add(animal);
...could mean two things:
add an Animal and a new AnimalCategory, the latter referring to the Region.
Add an Animal referring to an existing AnimalCategory - without being able to choose which one.
EF wouldn't want to choose for some default behavior. You'd have to make the choice yourself, so you can't do without access to AnimalCategory.

Entity Types not mapped but stored

I've a problem mapping just one table from database, I add several tables using "Update model from Database" function through Visual Studio 2010 interface, and everyone works as expected except one table!
Looking at "Model Browser" I can see the table doesn't appear under "Entity Types" but it is present under section "ObjectEntity.Store", so my POCO Generator create an entity related and I can't try to add it again from database but I can't access it through context (like context.table_name).
Tha table doesn't have particular form, there are just two key fields
Could someone help me?
Thanks
I guess it is junction table for implementing many-to-many relation, isn't it? In such case it is correct behavior. EF will hide this table because it is not needed in object oriented approach where many-to-many association can be modeled directly without helper entity. You will see in your model that those two related entities are connected by line with * - * multiplicity and each entity will contain navigation property which is collection of related entities. By manipulation with entities in these collections you are creating or removing records in that hidden table. That is the way how you work with such relation in EF.

Trouble inheriting from another entity

I'm having trouble configuring entity relationships when one entity inherits from another. I'm new to ADO Entity Framework -- perhaps someone more experienced has some tips for how this is best done. I'm using .net 4.
Database tables with fields:
Products (int ID, nvarchar Description)
FoodProducts (int ProductID, bit IsHuge)
Flavors (int ID, int FoodProductID, nvarchar Description)
There are constraints between Products and FoodProducts as well as FoodProducts and Flavors.
Using the designer I create a model from the database. The designer seems to get it right, with a 1:0..1 association between Product and FoodProduct entities, and 1:* association between Flavor and FoodProduct. No errors when I save or build.
Next I set FoodProduct entity to inherit from Product entity. Then I get errors concerning relationship between Product and FoodProduct. Ok, starting fresh, I first delete the relationship between Product and FoodProduct before setting the inheritance. But now I get errors about the relationship between FoodProduct and Flavor. So I delete and then recreate that relationship, connecting Flavor.ID to FoodProduct.ProductID. Now I get other errors.
My question is this: Should I instead be creating relationship between Flavor.FoodProductID and Product.ID? If so, I assume I then could (or should) delete the FoodProduct.ProductID property. Since my database will have many of these types of relationships, am I better off first creating the entity model and exporting the tables to SQL, or importing the database schema and then making many tweaks?
My intent is that there will be several types of products, some of which require many additional fields, some of which do not. So there may be zero or one FoodProducts records associated with each Product record. At least by my thinking, the table for each sub-type (FoodProducts) should be able to "borrow" the primary key from Products (as a FK) to uniquely identify each of its records.
You can find a screen capture here: http://img218.imageshack.us/img218/9720/entityframework.jpg (I'd embed the img but haven't earned the requisite rep' yet!)
Well, I deleted the FoodProduct.ProductID field, as it should always return the same value as Product.ID anyway. Then, as you hinted, I had to manually map the Products.ID field to FoodProducts.ProductID field. Errors resolved. I'll write a little code to test functionality. Thanks for the "observations"!
Couple of observations:
FoodProducts needs a primary key (e,g identity - FoodProductID). Are you sure it should be a 1:0..1 between Food and FoodProducts? I would have thought it should be 1:0..*. For this cardinality to work you need a unique PK on this table.
When you setup inheritance for entities, the parent entity's properties are inherited. So FoodProducts will inherit ID from the Product table.
BUT, on the physical model (database), this field still needs to be mapped to a column on the FoodProducts table - which is why you need the identity field.
After you setup inheritance, you still need to map all the columns on the derived tables. My money is on you have not mapped "ID" on FoodProducts to any column.
If you screencapped your model and show the errors you are getting it would be much easier to diagnose the issue.

Entity Framework many-to-many question

Please help an EF n00b design his database.
I have several companies that produce several products, so there's a many-to-many relationship between companies and products. I have an intermediate table, Company_Product, that relates them.
Each company/product combination has a unique SKU. For example Acme widgets have SKU 123, but Omega widgets have SKU 456. I added the SKU as a field in the Company_Product intermediate table.
EF generated a model with a 1:* relationship between the company and Company_Product tables, and a 1:* relationship between the product and Company_Product tables. I really want a : relationship between company and product. But, most importantly, there's no way to access the SKU directly from the model.
Do I need to put the SKU in its own table and write a join, or is there a better way?
I just tested this in a new VS2010 project (EFv4) to be sure, and here's what I found:
When your associative table in the middle (Company_Product) has ONLY the 2 foreign keys to the other tables (CompanyID and ProductID), then adding all 3 tables to the designer ends up modeling the many to many relationship. It doesn't even generate a class for the Company_Product table. Each Company has a Products collection, and each Product has a Companies collection.
However, if your associative table (Company_Product) has other fields (such as SKU, it's own Primary Key, or other descriptive fields like dates, descriptions, etc), then the EF modeler will create a separate class, and it does what you've already seen.
Having the class in the middle with 1:* relationships out to Company and Product is not a bad thing, and you can still get the data you want with some easy queries.
// Get all products for Company with ID = 1
var q =
from compProd in context.Company_Product
where compProd.CompanyID == 1
select compProd.Product;
True, it's not as easy to just navigate the relationships of the model, when you already have your entity objects loaded, for instance, but that's what a data layer is for. Encapsulate the queries that get the data you want. If you really want to get rid of that middle Company_Product class, and have the many-to-many directly represented in the class model, then you'll have to strip down the Company_Product table to contain only the 2 foreign keys, and get rid of the SKU.
Actually, I shouldn't say you HAVE to do that...you might be able to do some edits in the designer and set it up this way anyway. I'll give it a try and report back.
UPDATE
Keeping the SKU in the Company_Product table (meaning my EF model had 3 classes, not 2; it created the Company_Payload class, with a 1:* to the other 2 tables), I tried to add an association directly between Company and Product. The steps I followed were:
Right click on the Company class in the designer
Add > Association
Set "End" on the left to be Company (it should be already)
Set "End" on the right to Product
Change both multiplicities to "* (Many)"
The navigation properties should be named "Products" and "Companies"
Hit OK.
Right Click on the association in the model > click "Table Mapping"
Under "Add a table or view" select "Company_Product"
Map Company -> ID (on left) to CompanyID (on right)
Map Product -> ID (on left) to ProductID (on right)
But, it doesn't work. It gives this error:
Error 3025: Problem in mapping fragments starting at line 175:Must specify mapping for all key properties (Company_Product.SKU) of table Company_Product.
So that particular association is invalid, because it uses Company_Product as the table, but doesn't map the SKU field to anything.
Also, while I was researching this, I came across this "Best Practice" tidbit from the book Entity Framework 4.0 Recipies (note that for an association table with extra fields, besides to 2 FKs, they refer to the extra fields as the "payload". In your case, SKU is the payload in Company_Product).
Best Practice
Unfortunately, a project
that starts out with several,
payload-free, many-to-many
relationships often ends up with
several, payload-rich, many-to-many
relationships. Refactoring a model,
especially late in the development
cycle, to accommodate payloads in the
many-to-many relationships can be
tedious. Not only are additional
entities introduced, but the queries
and navigation patterns through the
relationships change as well. Some
developers argue that every
many-to-many relationship should start
off with some payload, typically a
synthetic key, so the inevitable
addition of more payload has
significantly less impact on the
project.
So here's the best practice.
If you have a payload-free,
many-to-many relationship and you
think there is some chance that it may
change over time to include a payload,
start with an extra identity column in
the link table. When you import the
tables into your model, you will get
two one-to-many relationships, which
means the code you write and the model
you have will be ready for any number
of additional payload columns that
come along as the project matures. The
cost of an additional integer identity
column is usually a pretty small price
to pay to keep the model more
flexible.
(From Chapter 2. Entity Data Modeling Fundamentals, 2.4. Modeling a Many-to-Many Relationship with a Payload)
Sounds like good advice. Especially since you already have a payload (SKU).
I would just like to add the following to Samuel's answer:
If you want to directly query from one side of a many-to-many relationship (with payload) to the other, you can use the following code (using the same example):
Company c = context.Companies.First();
IQueryable<Product> products = c.Company_Products.Select(cp => cp.Product);
The products variable would then be all Product records associated with the Company c record. If you would like to include the SKU for each of the products, you could use an anonymous class like so:
var productsWithSKU = c.Company_Products.Select(cp => new {
ProductID = cp.Product.ID,
Name = cp.Product.Name,
Price = cp.Product.Price,
SKU = cp.SKU
});
foreach (var
You can encapsulate the first query in a read-only property for simplicity like so:
public partial class Company
{
public property IQueryable<Product> Products
{
get { return Company_Products.Select(cp => cp.Product); }
}
}
You can't do that with the query that includes the SKU because you can't return anonymous types. You would have to have a definite class, which would typically be done by either adding a non-mapped property to the Product class or creating another class that inherits from Product that would add an SKU property. If you use an inherited class though, you will not be able to make changes to it and have it managed by EF - it would only be useful for display purposes.
Cheers. :)

Entity Framework won't SaveChanges on new entity with two-level relationship

I'm building an ASP.NET MVC site using the ADO.NET Entity Framework.
I have an entity model that includes these entities, associated by foreign keys:
Report(ID, Date, Heading, Report_Type_ID, etc.)
SubReport(ID, ReportText, etc.) - one-to-one relationship with Report.
ReportSource(ID, Name, Description) - one-to-many relationship with Sub_Report.
ReportSourceType(ID, Name, Description) - one-to-many relationship with ReportSource.
Contact (ID, Name, Address, etc.) - one-to-one relationship with Report_Source.
There is a Create.aspx page for each type of SubReport. The post event method returns a new Sub_Report entity.
Before, in my post method, I followed this process:
Set the properties for a new Report entity from the page's fields.
Set the SubReport entity's specific properties from the page's fields.
Set the SubReport entity's Report to the new Report entity created in 1.
Given an ID provided by the page, look up the ReportSource and set the Sub_Report entity's ReportSource to the found entity.
SaveChanges.
This workflow succeeded just fine for a couple of weeks. Then last week something changed and it doesn't work any more. Now instead of the save operation, I get this Exception:
UpdateException: "Entities in 'DIR2_5Entities.ReportSourceSet'
participate in the 'FK_ReportSources_ReportSourceTypes' relationship.
0 related 'ReportSourceTypes' were found. 1 'Report_Source_Types' is expected."
The debug visualizer shows the following:
The SubReport's ReportSource is set and loaded, and all of its properties are correct.
The Report_Source has a valid ReportSourceType entity attached.
In SQL Profiler the prepared SQL statement looks OK. Can anybody point me to what obvious thing I'm missing?
TIA
Notes:
The Report and SubReport are always new entities in this case.
The Report entity contains properties common to many types of reports and is used for generic queries. SubReports are specific reports with extra parameters varying by type. There is actually a different entity set for each type of SubReport, but this question applies to all of them, so I use SubReport as a simplified example.
I realise I'm late to this, but I had a similar problem and I hacked through it for about 3 hours before I came up with a solution. I'd post code, but it's at home - I can do it later if someone needs it.
Here are some things to check:
Set a breakpoint on the SaveChanges() call and examine the object context in depth. You should see a list of additions and changes to the context. When I first looked, I found that it was trying to add all my related objects rather than just point to them. In your case, the context might be trying to add a new Report_Source_Type.
Related to the previous point, but if you're retrieving the report source, make sure it is being retrieved from the database by its entity key and properly attached to the context. If not, your context might believe it to be a new item and therefore its required relationships won't be set.
From memory, I retrieved my references using the context.GetObjectByKey method, and then explicitly attached those objects to the context using the context.Attach method before assigning them to the properties of my original object.
I got this error because the table didn't have a primary key, it had a FK reference, but no PK.
After adding a PK and updating the model all is well.
Check if your ReportSource was loaded with the NoTracking option or if its EntityState == 'Detached'. If so, that is your problem, it must be loaded in the context.
This tends to happen if your database tables have a 1 - 1 relationship with each other. In your example reportsourceset expects a reportsorttypes with whatever id it is referencing. I have run into this problem when my relationship is linking two primary keys from opposite tables together.
I've got the same error because of new object instance which created "behind the scene" in "Added" state. This was not obvious.
I got this error when I added the new entity to the context but forgot to add the new entity to its parent's collection in the object graph.
For example:
Pet pet = new Pet();
context.Pets.Add(pet);
// forgot this: petOwner.Pets.Add(pet);