I am having trouble getting Entity Framework 4 to handle a bulk update in a m2m join. I have several many to many joins in my model, for example something like
Practice
PracticeID PK
Name...
PracticeSpecialties (join table)
PracticeID PK
SpecialtyID PK
Specialties
SpecialtyID pk
Name...
Pretty basic and EF 4 handles it well. My problem is I need to be able to "merge" 2 or more Specialties into 1, for example "Pediatrics" and "Children" and "Adolescents" should all be the same item. So if Pediatrics has a SpecialtyID of 1 and Children = 3 and Adolescents = 9 the query should perform an update on all rows in PracticeSpecialties where SpecialtyID IN (3, 9) and change the value to 1.
I can write a stored proc that would update do this all rows in the join table containing one of the undesired SpecialtyIDs then delete all the now orphaned Specialties, but if possible I am trying to stick with the EF pattern.
Any guidance is appreciated.
In entity framework you must do it in object way = you can simply modify junction table. You must work with objects in navigation properties. Your scenario will look like:
var children = context.Specialities.Include("Practices")
.Single(s => s.Name == "Children");
var pediatrics = context.Specialities.Include("Practices")
.Single(s => s.Name == "Pediatrics");
foreach (var practice in children.Practices)
{
pediatrics.Practices.Add(practice);
}
children.Practices.Clear();
context.Specialities.DeleteObject(children);
context.SaveChanges();
You should also override Equals and GetHashCode in Practice entity and use HashSet for Speciality.Practices (in case of POCOs). It will handle duplicities for you.
Result of this will be multiple deletes and inserts to junction table. This is EF way to do that.
Do not use many-to-many relationships. The join table ends up holding more data eventually anyway. Use a one-to-many and a many-to-one combination instead. If you want, you can make a property that holds the created/modified dates in the middle entity for now.
Hope this helps.
Related
trying to work out how to do LINQ to Entities query for a many to many relationship which has a junction with fields table.
Below are the Domains models (I am using View models, but keeping it simple for this example).
Student Domain model
StudentID (PK)
ICollection<StudentCourse> StudentCourses
StudentCourse Domain model
StudentCourseID (PK)
StudentID (FK)
CourseID (FK)
ForAdult
ForSeniour
Description
Course Domain model
CourseID (PK)
ICollection<StudentCourse> StudentCourses
Note:
Since the junction table (i.e. StudentCourse) contains fields other than the two foreign keys, EF will create an entity for this.
Lazy Loading
I've got this working for lazy loading. The Navigation properties have been declared with the 'virtual' keyword.
The Query way - works!
var student = (from s in context.Students
where s.StudentID == id
select s).SingleOrDefault<Student>()
The Method way - works!
Student student = context.Students.Find(id);
Projection
BUT, I would prefer to do this with projection, for performance reasons, i.e. less trips to the database.
I'm really stuck on how to write up the LINQ to Entities query to return 1 student with (1 or) many StudentCourses.
I don't understand thoroughly how the Entity should be shaped, if you know what I mean.
For example, I've tried:
var myvar = from s in context.Students
from sc in s.StudentCourses
where s.StudentID == id
select s
What I require is to return an entity of Student with a collection of StudentCourses which could then be assigned to a Student and passed to the View model, then to the View.
Really would appreciate any help, as I've spent alot of time trying to solve this.
Also as a side note, I'm using the SingleOrDefault() method to cast the results of the var (IQueryable I think) to type Student. Is this the preferred way to cast?
You can get EF to eagerly load the related entities by using the Include method.
So using your LINQ example:
var student = (from s in context.Students
where s.StudentID == id
select s).Include("StudentCourses").FirstOrDefault();
And using extension methods:
var student = context.Students.Include("StudentCourses").FirstOrDefault(id);
The Student instance that is returned will have the StudentCourses collection populated with related entities. This should invoke only one SQL query that joins the tables together.
To answer your aside question: I prefer to use FirstOrDefault most of the time as above. The difference is that SingleOrDefault will expect exactly one result and throws an exception otherwise, whereas FirstOrDefault will return null if a student is not found.
Also, as the cast to Student is implicit, you don't need the <Student> type parameter.
Ok, lets say you have two tables: Order and OrderLine and for some reason they do not have a foreign key relationship in the database (it's an example, live with it). Now, you want to join these two tables using Entity Framework and you cook up something like this:
using (var model = new Model())
{
var orders = from order in model.Order
join orderline in model.OrderLine on order.Id equals orderline.OrderId into orderlines
from ol in orderlines.DefaultIfEmpty()
select new {order = order, orderlines = orderlines};
}
Now, the above will produce orders and orderlines, left-joined and all, but it has numerous issues:
It's plain ugly
It returns an anonymous type
It returns multiple instances of the same order and I you have to do Distinct() on the client side because orders.Distinct() fails.
What I am looking for is a solution which is:
Pretty
Returns a statically well-known type instead of the anonymous type (I tried to project the query result, but I got into problems with the OrderLines)
Runs Distinct on the server side
Anyone?
Even if the database tables do not have a foreign key relationship setup, you can configure Entity Framework as if they do.
Add an OrderDetails navigation property to your Order class and then just query Orders.
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. :)
In some scenarios I need a "wide" version of an entity with many properties (say FullCustomer) while in other scenarios it's enough with a "narrow" version with few properties (say MiniCustomer), how could I map these two entities, FullCustomer and MiniCustomer, to the same Customer table in the database? Furthermore, I need to be able to query and update both entities.
Thanks in advance for any advice or pointers!
PD. I'm using VS2010 RC and EF 4
For "efficiency", don't have two versions of the entity; just project onto "lite" POCOs:
var q = from e in Context.Entities
select new LitePoco
{
Id = e.Id,
EditThis = e.EditThis
};
No other columns will be returned.
Similarly for save:
var e = new MyEntity { Id = 123 };
Context.AttachTo("Entities", e);
// anything from here on gets saved
e.EditThis = "Edited";
Context.SaveChanges();
If you have a column that determines whether to treat the record as a FullCustomer or as MiniCustomer, then you can easily create a Table per Hierarchy inheritance model with FullCustomer inherited from MiniCustomer. Such approach is described in the Muhammad Mosa's blog post.
If you don't have this column you can update the model and database in order to add it.
However, there is an alternative approach. If the reason to have two entities for one table is the delay in the loading the numerous large properties in the FullCustomer object, than Table Splitting might be an option. Take a look at the Julie Lerman's blog post.
I would have noted that this problem is not present in ORMs like LINQ to SQL, due to deferred loading.
I have 3 entities
-Direction
-City
-GeoPosition
each Direction have a Geoposition, and each City have a collection of Geopositions (this represent a polygon)
I have 5 tables
-directions
-cities
-geopositions
-directionsgeopositions
-citiesgeopositions
and EF entities is this
alt text http://img192.imageshack.us/img192/5863/entitydesignerdiagram.png
each entity have function imports for insert, update, and delete
i have this error
Error 2027: If an EntitySet or AssociationSet includes a function mapping,
all related entity and AssociationSets in the EntityContainer must also define
function mappings. The following sets require function mappings: CitiesGeopositions, DepartmentsGeopositions.
I need function imports for the relation tables??
what is the problem?
The answer to your questions are, respectively:
Yes.
See (1).
The Entity Framework allows you to insert/update/delete via DML or stored procs, but it does not allow you to choose "both." If you are going to go to the stored proc route, you must supply procs for every sort of data modification the framework might need to do on an entity, including relation tables.
For a couple of days now, I have been wracking my brains and scouring the Interwebz for information about how to insert data into database intersection tables using the Entity Framework (EF). I’ve hit all the major players’ web sites and blogs and NO ONE has provided straightforward syntax on how to perform this. Out of the blue, the answer occurred to me and I was bound and determined to share this with as many people as I could to lessen the pain I went through.
Let’s set the stage. Assume we have a database relationship as such:
Students (StudentID(PK), StudentName, Gender)
Courses (CourseID(PK), CourseName, CourseDescription)
StudentsCourses (StudentID(PK, FK), CourseID(PK, FK))
For those of you familiar enough with EF, you know that when the relationships above are translated into an entity data model, the Students and Courses tables are created as entities, but the StudentsCourses table is not. This is because the StudentsCourses table does not contain any attributes other than the keys from the other two tables, so EF directly maps the many-to-many relationship between Students and Courses (EF is not limited in the way relational databases are in this respect.) and instead of an entity, translates the intersection table into an AssociationSet. If you weren’t aware of this behavior, check out these links for examples:
http://thedatafarm.com/blog/data-access/inserting-many-to-many-relationships-in-ef-with-or-without-a-join-entity/
http://weblogs.asp.net/zeeshanhirani/archive/2008/08/21/many-to-many-mappings-in-entity-framework.aspx
Now let’s assume that you want to register a current student (ID:123456) for new courses this semester (ENGL101, SOC102, and PHY100). In this case, we want to insert new records into the StudentsCourses table using existing information in the Students table and Courses table. Working with data from either of those tables is easy as they are both an entity in the model, however you can’t directly access the StudentsCourses table because it’s not an entity. The key to this dilemma lies with the navigation properties of each entity. The Student entity has a navigation property to the Course entity and vice versa. We’ll use these to create “records of association” as I like to call them.
Here’s the code sample for associating an existing student with existing courses:
using (var context = TheContext())
{
Student st = context.Students.Where(s => s.StudentID == “123456”).First();
st.Courses.Add(context.Courses.Where(c => c.CourseID == “ENGL101”).First());
st.Courses.Add(context.Courses.Where(c => c.CourseID == “SOC102”).First());
st.Courses.Add(context.Courses.Where(c => c.CourseID == “PHY100”).First());
context.Students.AddObject(st);
context.SaveChanges();
}
Because the associations go both ways, it stands to reason that one could retrieve three Course objects (by CourseID) and associate the same Student object to each, but I haven’t tested that myself. I think it would result in more code than is necessary and might be semantically confusing.
Here’s a code sample that associates a new student with the same existing courses:
using (var context = TheContext())
{
Student st = new Student({ StudentID = “654321”, StudentName = “Rudolph Reindeer”,
Gender = “Male” });
st.Courses.Add(context.Courses.Where(c => c.CourseID == “ENGL101”).First());
st.Courses.Add(context.Courses.Where(c => c.CourseID == “SOC102”).First());
st.Courses.Add(context.Courses.Where(c => c.CourseID == “PHY100”).First());
context.Students.AddObject(st);
context.SaveChanges();
}
And finally, here’s the code to associate a new student with new courses (‘...’ used for brevity):
using (var context = TheContext())
{
Student st = new Student({ ... });
st.Courses.Add(new Course({ ... }));
st.Courses.Add(new Course({ ... }));
st.Courses.Add(new Course({ ... }));
context.Students.AddObject(st);
context.SaveChanges();
}