I am new to the Entity Framework and am looking for some direction on creating the relationships between an entity and related many-to-many associative entities and inserting them in one operation.
The relevant entities in my EDMX:
Participant
ID
Name
ParticipantCustomField
ParticipantID
CustomFieldID
Value
CustomField
ID
Name
I need to insert a single Participant entity and many ParticipantCustomField entities. The related CustomField entity will already be in the database at the time of insert.
I have a repository create method which accepts a Participant and a collection of ParticipantCustomField objects:
public Participant CreateParticipant(Participant participant, List<ParticipantCustomField> customFields)
{
// need to establish relationship here
entities.AddToParticipant(participant);
entities.SaveChanges();
return participant;
}
I have tried several methods but cannot figure out how to properly relate the collection of ParticipantCustomField objects with the new Participant before the insert. I know the CustomFieldID foreign key as that is set outside of this method, but the ParticipantID foreign key cannot be set until the Participant is inserted.
I guess since this is the Entity Framework I shouldn't be focused on "foreign keys", which I think are only there because my associative table has a third column, but on relations.
Thanks for any help!
You don't need to set ParticipantCustomField.ParticipantId. The framework will do that for you. Instead you'd do something like:
foreach (var cf in customField)
{
participant.CustomFields.Add(cf);
}
entities.AddToParticipant(participant);
entities.SaveChanges();
return participant;
I'm making some presumptions about your mappings here, but this should give you the general idea.
Related
My application allows the user to create a hierarchy of new entities via a UI - let's say it's a "Customer" plus one or more child "Order" entities. The user also assigns each Order entity to an existing "OrderDiscount" entity (think of these as "reference"/"lookup" items retrieved from the database). Some time later, the user will choose to save the whole hierarchy to the database, accomplished like this:-
using (var context = new MyContext())
{
context.Customers.Add(customer);
foreach (var entity in context.OrderDiscounts.Local)
{
objectStateManager.ChangeObjectState(entity, EntityState.Unchanged);
}
context.SaveChanges();
}
The foreach loop changes the state of the OrderDiscount entities to Unchanged, and prevents EF from attempting to insert them into the database, resulting in duplicates.
Great so far, but I've now hit another issue. For reasons I won't go into, the OrderDiscount entities can come from different BLL calls, resulting in a situation where two Orders in the graph may appear to reference the same OrderDiscount (i.e. both have the same PK ID, and other properties), but the entities are different object references.
When I save, the above foreach loop fails with the message "AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges". I can see the two OrderDiscount objects in the context.OrderDiscounts.Local collection, both with the same PK ID.
I'm not sure how I can avoid this situation. Any suggestions?
This article (http://msdn.microsoft.com/en-us/magazine/dn166926.aspx) describes the scenario and provides one possible solution, which is to set just the FK ID (order.OrderDiscountId), and leave the order.OrderDiscount relationship null. Unfortunately it's not feasible in my case, as further down the line I rely on being able to traverse such relationships, e.g. ApplyDiscount(order.OrderDiscount);.
I'm using this trick to perform conditional Include's with EF. http://blogs.msdn.com/b/alexj/archive/2009/10/13/tip-37-how-to-do-a-conditional-include.aspx
The problem I'm having is that any collections that don't have records, are null, and not empty. This is causing headaches cos I have to check each collection before I can loop through it in my mvc view, otherwise i get a null reference exception.
For example, the StudentModules collection will be null. How can I turn it into an empty list in my query? ie without having to loop through it all and checking.
I can put a constructor in the poco to initialize the list, which fixes it, but the this collection is a virtual member in the poco (based on an EF video!) - surely this is not the way to go?
var query = from module in db.Modules
where module.Id == id
select new
{
module,
QualificationModules = from qualificationModule in module.QualificationModules
where qualificationModule.IsDeleted == false
select new
{
qualificationModule,
qualificationModule.Qualification,
StudentModules = from studentModule in qualificationModule.StudentModules
where studentModule.IsDeleted == false
select new
{
studentModule,
studentModule.Student
}
},
Assessments = (from assessment in module.Assessments
where assessment.IsDeleted == false
select new
{
assessment,
assessment.AssessmentType
}
)
};
var modules = query.AsEnumerable().Select(x => x.module);
return modules.ToList().First();
Relationship fixup runs when an entity gets attached to a context - either manually by calling Attach or when the entity is materialized as a result of a query (your case).
It is based on foreign keys of an entity and works in both directions:
If the context already contains an entity A with a foreign key f to entity B and an entity B is being attached to the context that has a primary key with the same value f as the foreign key in A (i.e. the two entities are related by an FK relationship) then Entity Framework will do the following:
If A has a navigation reference property to B it will assign the attached entity B to this property.
If B has a navigation reference property to A (one-to-one relationship) it will assign A to this property.
If B has a navigation collection property to A (one-to-many relationship) it will add A to this collection in the attached entity B. If the collection is null it will instantiate the collection before adding.
If an entity B is being attached to the context that has a foreign key f to an entity A that the context already contains and that has f as primary key EF will set the navigation properties based on the same rules like above.
As a side note: The fact that relationship fixup is based on foreign keys (they are always loaded when you query an entity, no matter if the FK is exposed as property in the model class or not) is also the reason why relationship fixup does not apply to and does not work for many-to-many relationships because the two entities of a many-to-many relationship don't have a foreign key.
Now, if there are no related StudentModules in your case there is no StudentModule entity that gets loaded into the context and there is nothing what EF could target for a fixup. Keep in mind that the fixup algorithm is not related to a particular query and does not only fix relationships between entities that this query would materialize but it will consider all entities for fixup that the context already contains, no matter how they came into the context. If you would want that collections get instantiated as empty collections EF had run through all attached parent entities of StudentModules and just create an empty collection. It makes no sense to do this during fixup instead of creating empty collections up-front before entities get attached to a context.
I can put a constructor in the poco to initialize the list, which
fixes it, but the this collection is a virtual member in the poco
(based on an EF video!) - surely this is not the way to go?
In my opinion it is the best solution if you don't want to have null collections in your model class instances. It doesn't matter if the collection is declared as virtual (to enable lazy loading) or not. A collection type does not have a derived proxy type, only the instances that get added to the collection are derived proxies. In both case you can just use StudentModules = new HashSet<StudentModule>(); (or List if you prefer).
so the story is very simple.
I have one table called Products and another Called categories. In addition, i have another table called ProductCategories that hold the relationship of catetories to their corresponding products (i.e, the table has two columns, ProductId, ColumnId).
For some reason, after adding all those table to my entity model, i don't have "Access" to it, hence i can do myentityModel.ProductCategories, so i could relational items between those two tables.
And yes, the ProductCategores table is added as "Association" to the entity model. i don't really understand that.
EDIT:
I do see that as part of creating new "Product" i can pass EntityCollection of "Category". So i do query from my entity model for a list of the matching categories that the user selected (on the webpage). so for example, i get (after query the model), an Objectset of "Category". However, i encountered two issues:
the 'AddObject' accept only EntityCollection, hence i need to re-create a set and then add all the objects from the ObjectSet to the entityCollection, in this process i need to detach it from the previous model and add it to the new collection. if not, i get an exception.
when i do the SaveChanges, i see that i get an exception that it was actually trying to Create new Category rather than adding new ProductCategory. again, am i missing something here?
Thanks.
This sounds like a Many-to-Many relationship. In your entity model, you don't need to declare the join table as a separate entity. Instead, you configure the relationship between the Products and the Categories as a Many-to-Many and add metadata about the join table. In Hibernate, you would have:
#ManyToMany(targetEntity=Categories.class, cascade={CascadeType.ALL}, fetch = FetchType.LAZY)
#JoinTable(name="tb_products_categories",
joinColumns=#JoinColumn(name="category_id"),
inverseJoinColumns=#JoinColumn(name="product_id")
)
#IndexColumn(name="join_id")
public List<Categories> getCategories() {
return categories;
}
When you query, the ORM layer takes care of determining SQL and traversing table joins.
I am working with Entity Framework ... I have a database table for Patient which has a non-enforced foreign key relationship to the Employee table so I can associate a manager to a patient.
I created my entity in EF for the Patient, the Employee and an association between Patient and Employee, which I name to ManagerEmployee.
I also created another partial class for Patient that will allow me to easily get at the name of the employee from my business object class, also called Patient.
public string ManagerName
{
get { return this.ManagerEmployee == null ? string.Empty : this.ManagerEmployee.Name; }
}
So I have:
Patient Entity
Patient Partial Class
(to help with some of the data
retrieval)
Patient DTO (reads from
the Patient Entity)
The problem that I am having is that if the ManagerId (in this case is a Guid) does not related to an employee, or is not set (Guid.Empty) ... even though I am eager loading, it still makes another hit on the database.
IQueryable<Data.Patient> query = ctx.ObjectContext.Patients.Include("ManagerEmployee");
So if I have a 1000 records, that have this value set, all is well, but if the value for ManagerId is NOT set on any of these records, it makes 1+1000 database hits.
Wondering if anyone else has had this problem? There may be some bigger problem with the construction of my EF entities and/or associations, so I'm open to other suggestions.
Thanks!
This is now pretty old but in case you haven't already found the solution, my suggestion is to turn off lazy loading. What is most likely happening is that when you try to access a property that is null, lazy loading is happening. See
http://www.asp.net/entity-framework/tutorials/maximizing-performance-with-the-entity-framework-in-an-asp-net-web-application
if you're using database first, or
http://www.asp.net/entity-framework/tutorials/reading-related-data-with-the-entity-framework-in-an-asp-net-mvc-application
for MVC Code First.
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();
}