Tag Archives: Entity Framework

Entity Framework – Table Per Hierarchy (Single Table Inheritance)

In the past, I have worked with some ORM software such as X-tensive DataObject .NET, which generated the database schema based on the Entity models. This type of ORM is called Table-per-Type Inheritance, i.e. each table is mapped to single Entity in the model (*note 1).

However, this doesn’t always work if we have an existing database or we want to maximize the performance of the relational queries or we want to save space in database. In many cases, we don’t want the relational database to look like the Entity model.

Entity Framework supports Table-per-Hierarchy Inheritance, i.e.  different types in the Entity model that inherits from a base class and all mapped to a single table (*note 1).

This is an extension to the articles I found on the Internet:

(*note 1 were quotes from Moses’s Blog)

School Example

Let’s look at a very traditional “Person”, “Student” and “Instructor” example,  I have created a simple database. With “PersonTypeID” defined as a foreign key in a separated table and this is the major difference from the 2 links above.


In Visual Studio 2010,  I used the “Generate Model from Database” to create the initial EDMX file for my web application.  It helped 50% of the work and I strongly recommended.  Yet, there are  some manual work to do because Visual Studio doesn’t know what the conceptual level would look like.

 

At this point, it is pure relational-to-object. If you start coding in LINQ, it’s like LINQ-to-SQL.   However, our objective is to make it object oriented.  So, let’s add 2 new Entities in the design tool, namely “Student” and “Instructor” which inherits from base class “Person“.  Also, we have to delete the “HireDate” and “EnrollmentDate” from PERSON entity and reproduce the 2 properties.

If you compile right away, you will get the error:

Error 3005: Problem in mapping fragments starting at line xxx:Must specify mapping for all types in…. An Entity with Key (PK) will not round-trip when: Entity is type [xxxxxxxxx.yyyyyyyyy]

Conditional Mapping

To fix this, we have to add conditional mapping for the 2 Entities (Student and Instructor). This tells the engine to save the value 1 (PersonTypeID) to database (Person table) when we create a Person object.


Mapping Fragment Error

We now have the conditional mapping. Let’s compile again, but you will get this error:

Error 3032: Problem in mapping fragments starting at line xxx:Condition member ‘Person.PersonTypeID’ with a condition other than ‘IsNull=False’ is mapped. Either remove the condition on Person.PersonTypeID or remove it from the mapping.

In conceptual world, we don’t actually need to use “PersonTypeID” since we have already used conditional mapping to define the type of the person. So, we need to delete the “PersonalTypeID” from “Person” Entity.  Then, we need to delete the whole “PersonType” Entity too!  Here is the final model should look like:

If you are not using the “PeronalType” table to store the value, i.e. you are using integer or string, probably you may need to set the “Person” table to “Abstract”, otherwise you will get another annoying error:

Error 3023: Problem in Mapping Fragments starting at line xx,xx,xx : Column xxxx has no default value and is not nullable. A column value is required to store entity data.

Create object and Save to DB

You should now be able to compile without any error.  In the following sample ASPX code, we have created “Student” object and assigned some properties to it. Note that we have NEVER specified the “PersonalTypeID”.

using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Transactions;
using System.Data.Objects;
 
namespace EntityTest
{
    public partial class Test : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            SchoolEntities dataContext = new SchoolEntities();
 
            using (TransactionScope transactionScope = new TransactionScope())
            {
                Student student = new Student();
 
                student.EnrollmentDate = DateTime.Today;
                student.FirstName = "John";
                student.LastName = "Smith";
 
                dataContext.People.AddObject(student);
 
                dataContext.SaveChanges(SaveOptions.AcceptAllChangesAfterSave);
                transactionScope.Complete();
                dataContext.AcceptAllChanges();
            }
 
        }
    }
}

After running the above code, the “Student” is automatically saved with “PersonTypeID” assigned to “2″. It worked!

Posted in Development - .NET | Tagged , , | Leave a comment

Using LINQ Group By and String.Join() / Aggregate() in Entity Framework 3.5

“Group By” in LINQ with “String.Join(), Aggregate()” Problem

First of all, if you ever need to use “Group By” in LINQ on Entity Framework (EF), and you want to use “String.Join()” to convert a LIST to a delimited string, you may bump into this error easily:

  • “LINQ to Entities does not recognize the method ‘System.String Join(System.String, System.String[])’ method, and this method cannot be translated into a store expression. “

If you use “Aggregate()”, you will get another error message.

  • LINQ to Entities does not recognize the method ‘System.String Aggregate[String](System.Collections.Generic.IEnumerable`1[System.String], System.Func`3[System.String,System.String,System.String])’ method, and this method cannot be translated into a store expression.

The most annoying part is, this works fine in LINQ-To-SQL, but not LINQ-To-Entity.  After searching on the Internet, one of the solutions is to convert your object query using .ToList() and you can use String.Join(). Here is an example, Users and Roles are Entities in Data Context which are mapped as Many-To-Many (In relational database behind the scene, there are 3 tables).

For such data, we have:

jSmith Administrator
jSmith Users

We want to group them and display it in Grid:

jSmith Administrator, User

It is easy to do it in SQL and TSQL, however LINQ in Entity Framework, it is a little bit tricky.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 
var result1 = (from a in users
                   b in roles
                   where (a.RoleCollection.Any(x => x.RoleId = b.RoleId))
               group a by a.UserName into userGroup
               select new
               {
                  UserName = userGroup.FirstOrDefault().UserName,
                  RoleNames = userGroup.FirstOrDefault().RoleCollection.Select(x => x.RoleName)                 
               });
 
var result2 = (from a in result1.ToList()
               select new 
               {
                 UserName = a.UserName,
                 RoleNames = String.Join(", ",  a.RoleNames.ToArray())
               });

The above code actually worked fine when it was binded to the Grid, “RoleNames” showed properly by separated with a delimiter, i.e. “Administrator, User”.

Performance Issue

When I looked at the code, at first I didn’t find anything wrong with it. However when I threw 50,000 records on the Users table and ran the above code again, we got serious performance issue. It took so long for the page to load and everytime you flip to another page on the Grid, it took more than 20 seconds. That is totally not usable.

LINQ’s “Group By” does not directly translated into SQL’s “Group By”, they can be very expensive. Please refer to MSDN for details. Let’s take a look at the following by moving the LINQ group by to result2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 
var result1 = (from a in users
                    b in roles
               where (a.RoleCollection.Any(x => x.RoleId = b.RoleId))
               select new 
               {
                  UserName = a.UserName,
                  RoleNames = b.RoleName)                 
               });
 
var result2 = (from a in result1.ToList()
               group a by a.UserName into userGroup
               select new 
               {
                 UserName = userGroup.FirstOrDefault().UserName,
                 RoleNames = String.Join(", ", (userGroup.Select(x => x.RoleNames)).ToArray())
               });

This time, it only took 2 seconds to flip through each page on the Gird, with the same 50,000 records. That’s a 10 times difference.

To further improve on the speed, replace String.Join() with Aggregate():

1
2
3
4
5
6
 
// String.Join()
RoleNames = String.Join(", ", (userGroup.Select(x => x.RoleNames)).ToArray())
 
// Aggregate() 
RoleNames = (userGroup.Select(x => x.RoleNames)).Aggregate((a,b) => (a + ", " + b))

This improved the speed by 1 more second on getting 50,000 records.

Interesting Findings

In the 2nd code above, if you change “result1.ToList()” to “result1″ and then comment out “RoleNames” line like the following. The performance is going slower again, it took about 10 seconds to flip to next page on the Grid. Interesting…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 
var result1 = (from a in users
                    b in roles
               where (a.RoleCollection.Any(x => x.RoleId = b.RoleId))
               select new 
               {
                  UserName = a.UserName,
                  RoleNames = b.RoleName)                 
               });
 
var result2 = (from a in result1
               group a by a.UserName into userGroup
               select new 
               {
                 UserName = userGroup.FirstOrDefault().UserName,
                 // RoleNames = String.Join(", ", (userGroup.Select(x => x.RoleNames)).ToArray())
               });

It seems .ToList() and group by on the same query will make the performance faster, probably it translates to SQL properly.

If you have LINQ Profiler, you can see it behind the scene and then compare the 3 cases on SQL level. Unfortunately I don’t have such tool and I am pretty happy with the result, I ain’t gonna spend more time on this to further optimize it (perhaps it is possible).

Posted in Development - .NET | Tagged , , , , , , | 5 Comments