I have 2 entities: User and Company, with a FK from the User to the Company.
I'm trying to remove the association and leave the user entity with a scalar property "CompanyId", but still have the "Company" entity in the model (mainly to increase performance, I don't need to full entity attached to it).
I'm able to achieve that only by removing the association and then go to the edmx (xml) file and remove the leftovers manually, BUT...
After I regenerate the model (following additional changes in the schema etc.), I'm getting the "Company" association once again on the "User" object (along with the "CompanyId" property), which of course causes errors of mappings, since I'm having 2 mappings to the same CompanyId field in the database. Going once again to the xml to fix it is not something I'd like to do...
Is there a way around this? -Taking the "Company" table out to another model is not possible.
Thanks,
Nir.
I think I found the answer.
I can leave the entity association without the scalar property, and set it to a private getter. Then, add to the partial class the following:
public int CompanyId
{
get
{
return
(int)CompanyReference.EntityKey.EntityKeyValues.First(c => c.Key == "Id").Value;
}
}
That way I don't need to go to the database to fetch the company association along with the user, but I still have the value.
Nir.
Related
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!
I'm pretty new to the Entity framework and I'm modelling this simple structure:
With this model what I have is a Users class with a property UsersGroups (a collection of UserGroups objects).
I would like to have a Users class with a property like Groups with type Tuple or something like this (a new PriorizedGroup class, etc) that is much more related with the bussines.
Is this possible with the Entity framework?
Thanks in advance.
EDIT: If I were modeling the bussines objects I would create a User class with a Groups property that contained all the groups the user pertains with an extra property to store its priority (with a tuple, with an inherited class, as you wish). The thing is that I feel that the objects created by the Entity framework resemble the SQL structure, not the business structure.
Not directly. EF can map the relation only in the way you see it at the moment but you can add your custom behavior to your partial part of the entity. The simple way is something like
public partial class Users
{
public IEnumerable<PrioritizedGroup> Groups
{
get
{
return UserGroups.Select(ug => new PrioritizedGroup
{
Priority = ug.Priority,
Id = ug.Group.Id,
Name = ug.Group.Name,
Description = ug.Group.Description
})
.OrderBy(g => g.Priority);
}
}
}
To make this happen directly in EF you need some advanced mapping technique which will require you to modify EDMX source code directly (either DefiningQuery or QueryView) and it will make the entity read only (you will need stored procedures for modification).
To make the collection exposed on Users updatable you would probably need to use ObservableCollection and transfer all modifications triggered by ObservableCollection back to original UserGroups collection. Once you have something like that implemented you can hide original collection.
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.
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. :)
I have a table structure like the following:
Companies Addresses
********* *********
ID ID
AddressID ...
BillingAddressID ...
AddressID and BillingAddressID are foreign keys which are present in the Addresses table. When I generate my model based on this table instead of getting what I would expect to get (the AddressID, BillingAddressID) in the company class. I get the following:
public Addresses Addresses { .. }
public global::System.Data.Objects.DataClasses.EntityReference<Addresses> AddressesReference { .. }
public Addresses Addresses1 { .. }
public global::System.Data.Objects.DataClasses.EntityReference<Addresses> Addresses1Reference { .. }
It seems to be replacing BillingAddress with Addresses1 (not quite sure why that's happening). Also this seems to be common wherever I have a foreign key i.e. instead of the ID I get Table then the TableReference.
I think I can see whats happening i.e. instead of giving me the ID alone, it will be doing a lookup and finding the actual record the ID refers to. However, I am not quite sure what the TableReference field is for....
Can explain this a little better for me?
Thanks in advance.
Relationships are represented as objects in Entity Framework, in the same manner as entities. Even if you are not going to work a lot directly on them, relationship object are first class citizens in EF. EF kreates ObjectStateEntry objects for tracking changes on relationships, just like it does it for entities.
That is why there are two references. First one, AddressesReference is a reference to the relationship object, not the exact entity, and second one Addresses is actual entity.
Peter Chan (link), and Julia Lerman in her book Programming Entity Framework, 1st Edition, say that understanding how relationship works in EF is very important. Also they mention that this is first thing that is confusing developer when they start using EF.
The foreign keys are replaced by a reference to the entity (collection) the foreign key points to.
So to add an address to a company you would do something like:
Address a = new Address();
// ... set variables for address here
currentCompany.Addresses = a;
// or, the other way round (for collections)
a.Companies.Add(currentCompany);
EF uses the table names as the reference point when it builds the model and this is why you see "Addresses" and Addresses1". You can open up the entity model in the GUI format and click on each of the associations. These can be renamed to whatever you like, just click on the reference, view the mapping, ensure it is the one that maps "BillingAddressID" to "BillingAddressID" and rename that reference to "BillingAddress".
Note the current "Addresses" reference may be the one mapping the "BillingAddressID" so you have to check both references.
It would probably be best to change the mapping for "AddressID" to be "Address" instead of "Addresses" if it is a one to one mapping as well.