I am an experienced .NET developer but new to EF - so please bear with me. I will use an example of a college application to illustrate my problem. I have these user roles:
Lecturer, Student, Administrator.
In my code I envisage working with these entities as distinct classes so e.g. a Lecturer teaches a collection of Students. And work with 'is Student' 'TypeOf' etc.
Each of these entities share lots of common properties/methods e.g. they can all log onto the system and do stuff related to their role.
In EF designer I can create a base entity Person (or User...) and have Lecturer, Student and Administrator all inherit from that.
The difficulty I have is that a Lecturer can be an Administrator - and in fact on occasion a Student can be a Lecturer.
If I were to add other entities such as Employee and Warden then this gets even more of an issue.
I could presumably work with Interfaces so a person could implement ILecturer and IStudent, however I do not see how this fits within EF.
I would like to work within the EF designer if possible and I'm working model-first (coding in C#).
So any help and advice/samples would be very welcome and much appreciated.
Thanks
Don't make Student and Lecturer inherit from Person. As you say, what if "Bob" was both a student and a lecturer? This happens all the time in real colleges. You said it best yourself: These are roles, not types. A person can have many roles.
As a rule of thumb, avoid inheritance in O/R mapping when it's not strictly necessary (which is almost never). Just as when coding, favor composition over inheritance.
So you could give each Person a property Roles which is a 0..* collection of Roles. Then to get a list of students, you can do:
var students = from p in Context.People
where p.Roles.Any(r => r.Id = studentRoleId)
select p;
Or you could have a related Student type with a 0..1 relationship between Person and Student; this would allow you to add additional data for the student, e.g.:
var students = from p in Context.People
where p.StudentInfo != null
select new
{
Id = p.Id,
Name = p.Name,
Grades = p.Student.Grades
};
Related
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 am just starting to use the Entity Framework 4 for the first time ever. So far I am liking it but I am a bit confused on how to correctly do inheritance.
I am doing a model-first approach, and I have my Person entity with two subtype entities, Employee and Client. EF is correctly using the table per type approach, however I can't seem to figure out how to determine what type of a Person a specific object is.
For example, if I do something like
var people = from p in entities.Person select p;
return people.ToList<Person>();
In my list that I form from this, all I care about is the Id field so i don't want to actually query all the subtype tables (this is a webpage list with links, so all I need is the name and the Id, all in the Persons table).
However, I want to form different lists using this one query, one for each type of person (so one list for Clients and another for Employees).
The issue is if I have a Person entity, I can't see any way to determine if that entity is a Client or an Employee without querying the Client or Employee tables directly. How can I easily determine the subtype of an entity without performing a bunch of additional database queries?
Use .OfType<Client>() in your query to get just the clients. See OfType.
e.g. entities.Person.OfType<Client>() ...
Use is to test if a Person object is a specific sub-class, e.g. if (p is Employee) ...
BTW why isn't it entities.People? Did you not select the pluralization option?
Suppose I have
table Person
table Employee, which inherits Person.
I want to get a list of Person, regardless if this Person is Employee or not. How do you get entity framework to do that without joining the Employee table? C# is what I'm using. Thanks.
You need to make sure that you don't implicitly use any information from Employee.
If you do this:
var people = Context.People.ToList();
... Then me Entity Framework will initialize new instances of type Employee for those people who happen to be employees. This is because when you store an Employee in the database, you generally expect to get an Employee back when you select the same item.
Nor, for that matter, can you tell the Entity Framework to give you a Person when the stored value is an Employee. The Entity Framework will never give you back an incorrect entity type.
However, if you do just want to read the one table, there is a way to do that: Select the data from Person into a non-entity types, such as an anonymous type:
var people = Context.People.Select(p => new { Name = p.Name }).ToList();
I am using Entity Framework 3.5. I have created an object that calls "Persons" that is inherited from SQL Membership table (aspnet_Users), they are linked by UserId (Guid) with 1-to-(0..1) relationship and they both belong to "UserSet".
Say, I have an existing "aspnet_User" called "jsmith" in the membership database already. How can I create just the "Person" (child) that links it to "jsmith" in entity framework?
Guid guid = new Guid("xxxx-xxxx-xxx..") // jsmith Guid
User user = GetUserByGuid(guid); // Assuming a function gets "jsmith" as "User"
// Try 1:
Person person = new Person();
person.UserId = guid;
context.AddToUserSet(person);
context.SaveChanges(); // This doesn't work and throws an error
// Try 2:
Person.CreatePerson(person); // Doesn't work either, because it creates a whole new user
context.SaveChanges(); // Throws an error
I even tried to create an EntityKey and use detach/attach, didn't work either. Am I doing anything wrong? Any help is appreciated.
R.B.
You should not put SQL membership into your entity model. ASP.NET membership is intended to be pluggable; you can swap out the SQL membership provider for, say, an OpenID or domain authentication provider. Mapping the SQL membership tables makes you totally dependent on the specific implementation of one version of that provider, which is bad coupling.
Also, even if you did do this, inheritance is the wrong relationship. A user has an account. A user is not an account. So the correct relationship is composition, not inheritance.
Perhaps "Person" is the wrong relationship in the example. However, if you are talking about extending ASP.NET memembership by adding more properties like "FirstName", "LastName", etc.. this is not totally wrong. There are some articles talk about extending SQL membership and/or creating "Custom Membership" by adding your own db table and then inherited from base MembershipUser class. In that case, it is inheritance.
There are some articles that talks about extending membership, but not on Entity though:
http://www.code-magazine.com/Article.aspx?quickid=0703071
More articles
- codesmart.wordpress.com/2009/03/27/extending-the-microsoft-aspnet-membership-provider/
Also, you may want to consider using Profiles as alternative.
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();
}