Wednesday, January 02, 2008

I'm going to jump right into the Entity Framework here, with a simple how-to. If you want a gentle introduction into this technology, you can probably read some future posts, but you will be far better off reading the ADO.Net teamblog or better still, read all of the entries of Danny Simmons.

An OR-mapper needs to be flexible in mapping your domain classes, so you can describe your domain as truthfully as possible. The entity framework positions itself to be more than an OR-Mapper: it sees itself as a translation technology between models. One of those models is defined by your database, the other by your domain (your conceptual model). It's interesting to see what types of inheritance are possible. In this post I will give a sample of the Table Per Type (TPT) inheritance, where you define an inheritance in your conceptual model and use different tables to persist the data.

The way to implement this is fairly straightforward and documented, however, I certainly had some problems getting it to work. As did Scott Allen, so I thought I would create a small walk through for this scenario.

EF_TBT_DBDiagram.jpg

As you can see, my database (SqlExpress) has 3 tables. The 'Person' table, a customer table and an employee table. There are foreign key relations between them, with the Person table being the Primary/Unique Key Base Table and the Customer and Employee tables being the Foreign Key Base Tables. It is important to note that the PersonID column is set up to automatically generate ID's, and the CustomerID and EmployeeID columns are not: we want to create a situation where an object like Customer is persisted to these two tables (customer and person) and the Person table is the one supplying a new ID. The Customer table will just use that PersonID as it's own CustomerID.

That concludes the database schema, next up is the conceptual model, ugh, I mean domain model:

EF_TBT_ConceptualModel.jpg

This is how I want to my conceptual model to look like.  

When you have just generated the model from the database, the foreign key relations are visible. Remove them! Then, also remove the CustomerID and EmployeeID properties that were generated. Add the two inheritance links.
Since Customer already maps to Person (through the inheritance) you do not have to map these again. You do have to add a mapping to the customer table. The key point here is that the CustomerID column needs to be mapped to the PersonID property. Same goes for Employee.

Now, this is all there is to it. Before we dive into the XML generated, let me just point this out: when you update from the database again, the designer will fail. This is a major problem, where the synchronization just isn't good enough. It will encounter problems with the foreign key associations that it has recreated. I hope this gets fixed in the following CTP.

Now, although deceptively easy through the designer, I have a gut-feeling that it's going to be best to actually understand all the XML mapping that is generated. Just like the WPF and WF designers immediately made me turn to learning Xaml and Xoml, I feel that this designer is going to force me to learn CSDL and CS mapping pretty quickly.

When opening the edmx file with the XML editor, quickly use ctrl-E-F to format everything. The designer creates very long lines.
The SSDL content is unimportant, since it represents the database and should not be tinkered with here.
The CSDL is as follows:

    <edmx:ConceptualModels>
      <Schema Namespace="EntityFrameworkTestModel1" Alias="Self" xmlns="http://schemas.microsoft.com/ado/2006/04/edm">
        <EntityContainer Name="EntityFrameworkTestEntities4">
          <EntitySet Name="Person" EntityType="EntityFrameworkTestModel1.Person" />
        </EntityContainer>
        <EntityType Name="Customer" BaseType="EntityFrameworkTestModel1.Person">
          <Property Name="CustomerDiscount" Type="Int32" Nullable="false" />
        </EntityType>
        <EntityType Name="Employee" BaseType="EntityFrameworkTestModel1.Person">
          <Property Name="FunctionName" Type="String" Nullable="false" MaxLength="50" />
        </EntityType>
        <EntityType Name="Person">
          <Key>
            <PropertyRef Name="PersonID" />
          </Key>
          <Property Name="PersonID" Type="Int32" Nullable="false" />
          <Property Name="Firstname" Type="String" Nullable="false" MaxLength="50" />
          <Property Name="Lastname" Type="String" Nullable="false" MaxLength="50" />
        </EntityType>
      </Schema>
    </edmx:ConceptualModels>

Note a container with only one set: the Person set. Then the three types are defined. Only the Person type has a key identified, the Customer and the Employee do not!

The C-S mapping then, glues this model to the SSDL:

      <Mapping Space="C-S" xmlns="urn:schemas-microsoft-com:windows:storage:mapping:CS">
        <EntityContainerMapping StorageEntityContainer="dbo" CdmEntityContainer="EntityFrameworkTestEntities4">
          <EntitySetMapping Name="Person">
            <EntityTypeMapping TypeName="IsTypeOf(EntityFrameworkTestModel1.Person)">
              <MappingFragment StoreEntitySet="Person">
                <ScalarProperty Name="PersonID" ColumnName="PersonID" />
                <ScalarProperty Name="Firstname" ColumnName="Firstname" />
                <ScalarProperty Name="Lastname" ColumnName="Lastname" />
              </MappingFragment>
            </EntityTypeMapping>
            <EntityTypeMapping TypeName="IsTypeOf(EntityFrameworkTestModel1.Customer)">
              <MappingFragment StoreEntitySet="Customer">
                <ScalarProperty Name="PersonID" ColumnName="CustomerID" />
                <ScalarProperty Name="CustomerDiscount" ColumnName="CustomerDiscount" />
              </MappingFragment>
            </EntityTypeMapping>
            <EntityTypeMapping TypeName="IsTypeOf(EntityFrameworkTestModel1.Employee)">
              <MappingFragment StoreEntitySet="Employee">
                <ScalarProperty Name="PersonID" ColumnName="EmployeeID" />
                <ScalarProperty Name="FunctionName" ColumnName="FunctionName" />
              </MappingFragment>
            </EntityTypeMapping>
          </EntitySetMapping>
        </EntityContainerMapping>
      </Mapping>

Here we see the PersonID being mapped to either the CustomerID column or the EmployeeID column. Interesting!

In code, this allows us to do the following:

EntityFrameworkTestEntities4 context = new EntityFrameworkTestEntities4();


// inserting some
Person p = new Person();
p.Firstname = "Ruurd";
p.Lastname = "Boeke";
context.AddToPerson(p);

Customer c = new Customer();
c.Firstname = "Silvia";
c.Lastname = "Banana";
c.CustomerDiscount = 10;
context.AddToPerson(c);

Employee e = new Employee();
e.Firstname = "Ian";
e.Lastname = "Mort";
e.FunctionName = "Developer";
context.AddToPerson(e);

context.SaveChanges();

IQueryable<Person> persons =
from person in context.Person
select person;

foreach (Person person in persons)
{
Console.WriteLine("{0}, {1} is a {2}",
person.Firstname, person.Lastname, person.GetType().Name);
}

Console.ReadLine();

Did you notice that the context was first saved, before the Linq query was executed? It's easy to forget, but the query is executed on the database, not on a Union of objects in the database and in the context.

The output is as expected:

Ruurd, Boeke is a Person
Silvia, Banana is a Customer
Ian, Mort is a Employee

What I really like about this mapping strategy, is the fact that no discriminator column was needed. The system knows that an employee is an employee because of the existence of the record in the employee table. Let's quickly look at the generated query

SELECT
CASE WHEN (( NOT (([UnionAll1].[C2] = 1) AND ([UnionAll1].[C2] IS NOT NULL))) AND ( NOT (([UnionAll1].[C3] = 1) AND ([UnionAll1].[C3] IS NOT NULL)))) THEN '0X' WHEN (([UnionAll1].[C2] = 1) AND ([UnionAll1].[C2] IS NOT NULL)) THEN '0X0X' ELSE '0X1X' END AS [C1],
[Extent1].[PersonID] AS [PersonID],
[Extent1].[Firstname] AS [Firstname],
[Extent1].[Lastname] AS [Lastname],
CASE WHEN (( NOT (([UnionAll1].[C2] = 1) AND ([UnionAll1].[C2] IS NOT NULL))) AND ( NOT (([UnionAll1].[C3] = 1) AND ([UnionAll1].[C3] IS NOT NULL)))) THEN CAST(NULL AS int) WHEN (([UnionAll1].[C2] = 1) AND ([UnionAll1].[C2] IS NOT NULL)) THEN [UnionAll1].[CustomerDiscount] END AS [C2],
CASE WHEN (( NOT (([UnionAll1].[C2] = 1) AND ([UnionAll1].[C2] IS NOT NULL))) AND ( NOT (([UnionAll1].[C3] = 1) AND ([UnionAll1].[C3] IS NOT NULL)))) THEN CAST(NULL AS nvarchar(50)) WHEN (([UnionAll1].[C2] = 1) AND ([UnionAll1].[C2] IS NOT NULL)) THEN CAST(NULL AS nvarchar(50)) ELSE CAST( [UnionAll1].[C1] AS nvarchar(50)) END AS [C3]
FROM [dbo].[Person] AS [Extent1]
LEFT OUTER JOIN (SELECT
    [Extent2].[CustomerID] AS [CustomerID],
    [Extent2].[CustomerDiscount] AS [CustomerDiscount],
    CAST(NULL AS nvarchar(max)) AS [C1],
    cast(1 as bit) AS [C2],
    cast(0 as bit) AS [C3]
    FROM [dbo].[Customer] AS [Extent2]
UNION ALL
    SELECT
    [Extent3].[EmployeeID] AS [EmployeeID],
    CAST(NULL AS int) AS [C1],
    [Extent3].[FunctionName] AS [FunctionName],
    cast(0 as bit) AS [C2],
    cast(1 as bit) AS [C3]
    FROM [dbo].[Employee] AS [Extent3]) AS [UnionAll1] ON [Extent1].[PersonID] = [UnionAll1].[CustomerID]

A left outer join was done with a union of customer and employee. Also note the CASE statements in the main select statement.
I leave it up to you to decide if such SQL statements are okay in your environment. I see no problem with them but I can see them growing pretty fast.

Let's take a look at what happens if you only want your customers. The following Linq expression can be used:

IQueryable<Customer> customers =
from customer in context.Person.OfType<Customer>()
select customer;

When executed, the following SQL is executed:

SELECT
'0X0X' AS [C1],
[Extent1].[CustomerID] AS [CustomerID],
[Extent2].[Firstname] AS [Firstname],
[Extent2].[Lastname] AS [Lastname],
[Extent1].[CustomerDiscount] AS [CustomerDiscount]
FROM [dbo].[Customer] AS [Extent1]
INNER JOIN [dbo].[Person] AS [Extent2] ON [Extent1].[CustomerID] = [Extent2].[PersonID]

All in all very straightforward.

Wednesday, January 09, 2008 7:11:12 PM (Romance Standard Time, UTC+01:00)
Pingback from http://oakleafblog.blogspot.com/2008/01/linq-and-entity-framework-posts-for.html
Thursday, January 10, 2008 10:34:36 PM (Romance Standard Time, UTC+01:00)
Sorry for misspelling your first name! It's fixed at http://oakleafblog.blogspot.com/2008/01/linq-and-entity-framework-posts-for.html.

--rj
Friday, January 18, 2008 8:23:58 PM (Romance Standard Time, UTC+01:00)
When you say "You do have to add a mapping to the customer table. The key point here is that the CustomerID column needs to be mapped to the PersonID property. Same goes for Employee." are you doing this through the designer? How?
dml
Friday, January 18, 2008 8:25:52 PM (Romance Standard Time, UTC+01:00)
Oh.. and what do you do if you need to do a few hundred of these? There should be some way to automate this.
dml
Saturday, January 19, 2008 12:31:31 PM (Romance Standard Time, UTC+01:00)
Yes, I did it through the designer. Just select your customer entity and go to the mapping tab. Then map it to customer table. The customerid column needs to be mapped to the personid property, which should be selectable. If it's not selectable, you should check your hierarchy to see if you already mapped it.

Hey, if you have a few hundred of these, you are f*ed. However, you would be f*ed with every designer probably. I would go straight into the xml editor and do it there: much faster!

Ruurd
Ruurd
Thursday, November 25, 2010 12:45:38 PM (Romance Standard Time, UTC+01:00)
Hey, if you have a few hundred of these, you are f*ed. However, you would be f*ed with every designer probably. I would go straight into the xml editor and do it there: much faster!
Thursday, December 02, 2010 3:46:37 AM (Romance Standard Time, UTC+01:00)
This article was Very helpful. Actually, I am fond of reading online punjabi news. Thanks for writing such a complete ..
thank you for sharing.
Thursday, December 02, 2010 8:21:52 AM (Romance Standard Time, UTC+01:00)
xiaolaba Only after before a "concept, unlike other principles vile ugg boots on sale and difficult to understand, on the contrary boots uggs on sale, it is simple and easily arouses the child's confidence and high spirits, goals are small but specific ugg boot sale. In this goal, the child without understanding thoughts, solid, conscientious to do with bailey button, abandoned children in a meeting in surface. Small success to classic mini uggs, and gets the others recognized and appreciated, will gradually moving towards greater success mini uggs on sale, you'll make a big career. Words can ruin a man's confidence uggs mini on sale, even burst of his hopes of survival classic mini uggs on sale, But a word can also encourage one from loss to come out, or make one from a new Angle know yourself ugg classic cardy, changed his life. So at any time, we don't save said an encouraging word, give a trusting eyes ugg classic short, do a duck-yard trifle. A person's strength for himself perhaps is very limited ugg classic short navy, but he has probably help stimulate another person's infinite potential ugg boot sale.
Thursday, December 02, 2010 12:44:40 PM (Romance Standard Time, UTC+01:00)
Did you know that Skechers Resistance Runner has come out with a new line of Mens Skechers Resistance Runner? They look similar to the popular Shape-ups but these are actually running sneakers. The new Womens Skechers Resistance Runner retail for about $150. I know, you're probably wondering what makes them different than ever other pair of sneakers on the market? Let me break it down for you. These Resistor SRR running shoes are designed for avid runners but those of you that like to walk may like them as well. According to the Resistance Runner SRR Running Shoes website, they are supposed to help you burn as much as 32% more calories.Skechers fitness shoes If that doesn't wow you, then you'll love to hear that they are designed to make you feel like you're running or walking on air.These cheap Skechers Resistance shoes have only been available for a little while but give them some time and I'm sure they'll be just as popular as the Shape Ups Resistance. But you tell me. Do you think that these Resistance Runners will become a hit? Would you try them? Sound off in the comment section below.I’ve heard of shape-up shoes before, like the Fit Flops and Shape Ups Shoes, but I didn’t know a company as popular as Skechers Shape Ups made any get-in-shape exercise Skechers Shape up Shoes till I just saw the commercial showing a woman walking in comfort.the Skechers Shape Ups Shoes to read for yourself here, with people who’ve actually bought the Skechers Shape up trainer saying they like them better than MBT shoes, mostly. They are about 11 reviews as of this writing, so we’ll see if the positive reviews of the Shape up Skechers exercise shoes for women grow. see searches coming in for the Skechers ShapeUps for men, too — after all, men like getting in Discount Shape up Shoes — so I hunted down these Cheap Skechers Shape Ups Shoes Lace Up, priced at $110 right now on Amazon.Those shape ups outlet for men running? Skechers Womens Shape Ups — I guess you can run in them, huh? — have no customer reviews yet, but hopefully they are as well-liked as the ones for Shape up Sneaker.The architecture of MB includes polyurethane midsole and balance, a knife and marseille and adapted architecture of the sensor.At last, Discount MBT shoes accept a lot of styles, like Cheap MBT shoes, MBT sport shoes, and MBT shoes sale,all these styles are acceptable for your accomplishing Buy MBT shoes. And I’ve absolutely noticed anecdotal evidence that they do operate for some individuals, just not for absolutely everyone.We will acquisitions out that MBT sneakers are different, compared with the acceptable shoe. Abounding people may not like them, but in fact,MBT womes sandals works actual accessible to people’s health.

Saturday, December 04, 2010 2:44:23 AM (Romance Standard Time, UTC+01:00)
Clone world of golf clubs are a perfect option for everybody who is just beginning to have fun golf swing. There isn't any have to go out and pay out significant bucks to get a set in place of substantial halt playing golf clubs after you do not even know for those who will finally end up liking the game following your first few rounds about the back links. You may come to a decision golf swing just is not really to suit your needs. This just earns beneficial financial sense. When you perform with a arrangement of clone golf club clubs you'll hold the very same playability as the title manufacturer established, but using a far lessen price tag.
Saturday, December 04, 2010 2:51:17 AM (Romance Standard Time, UTC+01:00)
Improved and much more frequent play golf can be played by the weekend golfer by solely heeding some great tips and practicing sound discipline. But let's get again to our question: how do we develop a regular swing. You want to sustain a continuous golf swing plane in order to help keep your membership deal with sq..
Comments are closed.