July 2008 - Posts

Why does my French blog have more posts than my English one?

Some of you remark that I write some posts in French that I don't translate in English and ask me why. In fact it's very simple. When I actually write a post, I systematicly translate it but when I just mention a post I see in Julie's blog or Roger's blog, I don't because I suppose that people who read my English blog also read Julie's and Roger's.

If you think I should also mention it in my English blog, tell me so but I think that if you are interested in Entity Framework you should have a rss feed to Julie's blog, Roger's blog and all of the Entity Framework blogger (note that the site is now on construction), particularly EF Microsoftees blogs.

Posted by Matthieu MEZIL | with no comments

Daniel Simmons on DnrTV, part 2

Two weeks ago, drnTV proposed an Entity Framework introduction by Daniel Simmons.

Today, there is the part 2: EF ans ASP. Net, Data Serivces Framework (aka Astoria) and EF and a WCF service.

This second part also introduces a new web project template: "Dynamic Data Entities Web Application". This template generates for you a web site to read and to modify your datas.

Posted by Matthieu MEZIL | 1 comment(s)
Filed under: ,

EntityReference change key

From Northwind, I want to add the following method to Product class:

public void SetCategory(int categoryID)

{

    // ???

}

The problem is that from product class, I have no reference to the ObjectContext (which can be null if the product isn't attached). So it isn't possible to use Category property because we will have an exception if we set this property with a category attached to a different context than the product.

The idea is to use CategoryReference property. CategoryReference is of type System.Data.Objects.DataClasses.EntityReference<Categories> which contains a property EntityKey. We just need to change the EntityKey.

EntityKey has a property EntityKeyValues of type System.Data.EntityKeyMember[]. In our case, the key of Category is only the CategoryID. So we can be tempted to do this:

p.CategoryReference.EntityKey.EntityKeyValues[0].Value = categoryID;

but it isn't possible! Indeed EntityKeyMember is immutable, so you can't change its property Key or Value if they are affected.

So, the solution is to reinstanciate a new EntityKey:

CategoryReference.EntityKey = new EntityKey("NorthwindEntities.Categories", "CategoryID", categoryID);

Posted by Matthieu MEZIL | 2 comment(s)
Filed under: ,

EF and the crazy tester

I try to do some stupid EF tests.

From Northwind Product table, I did following inheritance:

<EntityType Name="Product">

    <Key>

        <PropertyRef Name="ProductID" />

    </Key>

    <Property Name="ProductID" Type="Int32" Nullable="false" />

    <Property Name="ProductName" Type="String" Nullable="false" MaxLength="40" Unicode="true" FixedLength="false" />

    <Property Name="SupplierID" Type="Int32" />

    <Property Name="CategoryID" Type="Int32" />

    <Property Name="QuantityPerUnit" Type="String" MaxLength="20" Unicode="true" FixedLength="false" />

    <Property Name="UnitPrice" Type="Decimal" Precision="19" Scale="4" />

    <Property Name="UnitsInStock" Type="Int16" />

    <Property Name="UnitsOnOrder" Type="Int16" />

</EntityType>

<EntityType Name="ProductReorder0" BaseType="NorthwindModel3.Product" >

    <Property Name="Discontinued" Type="Boolean" Nullable="false" />

</EntityType>

<EntityType Name="DiscontinuedProduct" BaseType="NorthwindModel3.Product" >

    <Property Name="ReorderLevel" Type="Int16" Nullable="true" />

</EntityType>

with this mapping:

<EntityTypeMapping TypeName="IsTypeOf(NorthwindModel3.ProductReorder0)">

    <MappingFragment StoreEntitySet="Products" >

        <ScalarProperty Name="Discontinued" ColumnName="Discontinued" />

        <ScalarProperty Name="UnitsOnOrder" ColumnName="UnitsOnOrder" />

        <ScalarProperty Name="UnitsInStock" ColumnName="UnitsInStock" />

        <ScalarProperty Name="UnitPrice" ColumnName="UnitPrice" />

        <ScalarProperty Name="QuantityPerUnit" ColumnName="QuantityPerUnit" />

        <ScalarProperty Name="CategoryID" ColumnName="CategoryID" />

        <ScalarProperty Name="SupplierID" ColumnName="SupplierID" />

        <ScalarProperty Name="ProductName" ColumnName="ProductName" />

        <ScalarProperty Name="ProductID" ColumnName="ProductID" />

        <Condition ColumnName="ReorderLevel" Value="0" />

    </MappingFragment>

</EntityTypeMapping>

<EntityTypeMapping TypeName="IsTypeOf(NorthwindModel3.DiscontinuedProduct)">

    <MappingFragment StoreEntitySet="Products" >

        <ScalarProperty Name="ReorderLevel" ColumnName="ReorderLevel" />

        <ScalarProperty Name="UnitsOnOrder" ColumnName="UnitsOnOrder" />

        <ScalarProperty Name="UnitsInStock" ColumnName="UnitsInStock" />

        <ScalarProperty Name="UnitPrice" ColumnName="UnitPrice" />

        <ScalarProperty Name="QuantityPerUnit" ColumnName="QuantityPerUnit" />

        <ScalarProperty Name="CategoryID" ColumnName="CategoryID" />

        <ScalarProperty Name="SupplierID" ColumnName="SupplierID" />

        <ScalarProperty Name="ProductName" ColumnName="ProductName" />

        <ScalarProperty Name="ProductID" ColumnName="ProductID" />

        <Condition ColumnName="Discontinued" Value="true" />

    </MappingFragment>

</EntityTypeMapping>

<EntityTypeMapping TypeName="NorthwindModel3.Product">

    <MappingFragment StoreEntitySet="Products">

        <ScalarProperty Name="ProductID" ColumnName="ProductID" />

        <ScalarProperty Name="UnitsOnOrder" ColumnName="UnitsOnOrder" />

        <ScalarProperty Name="UnitsInStock" ColumnName="UnitsInStock" />

        <ScalarProperty Name="UnitPrice" ColumnName="UnitPrice" />

        <ScalarProperty Name="QuantityPerUnit" ColumnName="QuantityPerUnit" />

        <ScalarProperty Name="CategoryID" ColumnName="CategoryID" />

        <ScalarProperty Name="SupplierID" ColumnName="SupplierID" />

        <ScalarProperty Name="ProductName" ColumnName="ProductName" />

        <Condition ColumnName="Discontinued" Value="false" />

    </MappingFragment>

</EntityTypeMapping>

So in fact, what do I want to do?

What happens if ObjectContext don't know which type instanciate? Indeed, with my DB, I have some products with Discontinued=true and ReorderLevel=0. So which type should ObjectContext instanciate? DiscontinuedProduct or ProductReorder0?

Of course, I have an EDM error but, as the compiler only use the generated code, I can work with this.

I execute following code for my test:

using (var context = new NorthwindEntities())

{

    var q = context.Products.OfType<DiscontinuedProduct>();

    foreach (var p in q)

        if (p.GetType() != typeof(Product))

            Console.WriteLine(p.GetType());

}

using (var context = new NorthwindEntities())

{

    var q = context.Products.OfType<ProductReorder0>();

    foreach (var p in q)

        if (p.GetType() != typeof(Product))

            Console.WriteLine(p.GetType());

}

using (var context = new NorthwindEntities())

{

    var q1 = context.Products.OfType<DiscontinuedProduct>();

    foreach (var p in q1)

        if (p.GetType() != typeof(Product))

            Console.WriteLine(p.GetType());

    var q2 = context.Products.OfType<ProductReorder0>();

    foreach (var p in q2)

        if (p.GetType() != typeof(Product))

            Console.WriteLine(p.GetType());

}

using (var context = new NorthwindEntities())

{

    var q = context.Products;

    foreach (var p in q)

        if (p.GetType() != typeof(Product))

            Console.WriteLine(p.GetType());

}

In the first two cases, there is no exception which is very interesting. This means that the OfType determines not only the sql request but also the entity instanciation.

In the third case, when I execute q2, we will have an exception. Indeed, the 3 entity types are linked to the same EntitySet. Each entity has an EntityKey which must be unique in the EntitySet. In the third case, the first Product with Discontinued = true and ReorderLevel = 0 will throw an exception.

In the last case, we will have an exception because ObjectContext doesn't know which entity type instanciate.

Posted by Matthieu MEZIL | with no comments
Filed under: , ,

Entity Framework: how to clone an entity?

In my last post, I wrote that the classic detachment/attachment pattern includes the clone of the entity.

But some of you didn't know how to clone an entity.

I see two choices:

  • you can use reflection (with optimization ability) as I did here
  • you can also use serialization / deserialization as Patrick Magee proposes here
Posted by Matthieu MEZIL | with no comments
Filed under: ,

Optimistic update

Here is the classic pattern of entities detachment / attachment:

We get the entity.
We send the entity to the client which clone it.
To update entity, the client send to the server the initial entity and the modified one.
Then, the server instanciates a new context, attaches the initial entity, call the ApplyPropertyChanges method with modified entity and then saves the context.

However, if you don't want to keep the possible updates of the DB row between the send to the client and the save of the server, you don't need the initial entity.
Before ApplyPropertyChanges, the initial entity must be on the context. Here, you can get the entity with latest DB row with TryGetObjectByKey method. Then, you just need to use ApplyPropertyChanges and SaveChanges methods to update the DB.

This sample is not a client/server code but it allows to check my solution.

NorthwindModel.Categories c;

NorthwindModel.Categories c2;

using (var context = new NorthwindModel.NorthwindEntities())

{

    context.Categories.MergeOption = System.Data.Objects.MergeOption.NoTracking;

    c = context.Categories.First();

}

using (var context = new NorthwindModel.NorthwindEntities())

{

    c2 = context.Categories.First();

    context.DeleteObject(c2);

    context.SaveChanges();

}

using (var context = new NorthwindModel.NorthwindEntities())

{

    object o;

    context.TryGetObjectByKey(c.EntityKey, out o);

    context.ApplyPropertyChanges("Categories", c);

    context.SaveChanges();

}

With attachment, ApplyPropertyChanges, the SaveChanges method do nothing if the DB row was deleted.
Previous code throws an exception on ApplyPropertyChanges method if DB row was deleted. But with this following code we have no exception and the DB row is created again.

NorthwindModel.Categories c;

NorthwindModel.Categories c2;

using (var context = new NorthwindModel.NorthwindEntities())

{

    context.Categories.MergeOption = System.Data.Objects.MergeOption.NoTracking;

    c = context.Categories.First();

}

using (var context = new NorthwindModel.NorthwindEntities())

{

    c2 = context.Categories.First();

    context.DeleteObject(c2);

    context.SaveChanges();

}

using (var context = new NorthwindModel.NorthwindEntities())

{

    object o;

    context.TryGetObjectByKey(c.EntityKey, out o);

    if (o == null)

        context.AddToCategories(c);

    else

        context.ApplyPropertyChanges("Categories", c);

    context.SaveChanges();

}

Posted by Matthieu MEZIL | with no comments
Filed under: ,

One table -> two entity types, relationship management

With my two last posts (ssdl view and TPT and ssdl view and TPH), I show you how to have two entity types mapped on one single table without discriminator column.

Now, next step. In my table Employees, I have a FK between Employees.EmployeeManager and Employees.EmployeeId. When I create my EDM from my DB, in my edm, I have a relationship between Employee entity type and itself.

In my CDSL, I add business logic with Manager and Employee distinction and I now want a relationship between Employee and Manager.

For this, I delete my csdl relationship, then my ssdl relationship (only possible by editing edmx xml myself). If I don't I will have an edmx error. If I delete the csdl relationship without edm designer (so by editing edmx xml), I must also delete the msl part of the relationship.

Then I add a relationship between Employee and Manager and I map it on Employees table.

Great it's ok! Smile

Now more difficult. If an employee become a manager, how to do this? So in fact, how to add some sub-employees to an employee?

Following code allow us to do this:

var manager = new Manager { EmployeeId = oldEmployee.EmployeeId, EmployeeManager = oldEmployee.EmployeeManager, EmployeeName = oldEmployee.EmployeeName };

ObjectStateEntry ose;

context.ObjectStateManager.TryGetObjectStateEntry(manager, out ose);

if (ose == null)

    context.AddToEmployeesWithManager(manager);

context.Detach(oldEmployee);

context.AcceptAllChanges();

manager.SubEmployees.Add(new Employee { EmployeeId = newEmployeeId, EmployeeName = newEmployeeName });

context.SaveChanges();

Great ! Now I will explain it.

When we set the manager EmployeeManager property, this action adds the manager entity to the context of oldEmployee.

But if the oldEmployee context isn't our context instance or if oldEmployee.EmployeeManager is null, we must add the new manager to our context instance.

That's why I use the TryGetObjectStateEntry test.

Then, I must detach oldEmployee because I will have some conflicts else.

Now, we have a manager instance so we can add some sub-employees. In DB, nothing should change for this record but if we do a SaveChanges, we will have a problem because manager instance has Added state. That's why I use AcceptAllChanges method.

Then, we can add new sub-employees and after this, we just need to save new employees into DB.

Posted by Matthieu MEZIL | with no comments
Filed under: , ,

ssdl view and TPT

I want to do the same than my last post with TPT.

I add this in ssdl:

<EntitySet Name="Managers" EntityType="TestModel1.Store.Managers">

    <DefiningQuery>

        SELECT DISTINCT EmployeeManager

        FROM EmployeesWithManager

    </DefiningQuery>

</EntitySet>

<EntityType Name="Managers">

    <Key>

        <PropertyRef Name="EmployeeManager" />

    </Key>

    <Property Name="EmployeeManager" Type="int" Nullable="false" />

</EntityType>

Then, I just need to create Manager entity type, add inheritance between it and Employee entity type, map EmployeeManager ssdl view with EmployeeId property and it's all.

It's too easy to use Entity Framework, isn't it? Wink

Posted by Matthieu MEZIL | with no comments
Filed under: , ,

ssdl view and TPH

This post uses the same idea than my last one: “Entity Framework: How to use Entity Splitting with different PK?”.

We have a table Employee with 3 columns: EmployeeId (PK), EmployeeName and EmployeeManager (FK to EmployeeId).

In my EDM, I want 2 entity types: Employee and Manager which inherits Employee.

How to do this?

The idea is to use a ssdl view to use TPH.

I just need to add this code in ssdl:

<EntitySet Name="EmployeeWithManagerStatus" EntityType="TestModel.Store.EmployeeWithManagerStatus">

    <DefiningQuery>

        SELECT EmployeeID, CAST(CASE WHEN EmployeeId IN (SELECT EmployeeManager FROM EmployeesWithManager) THEN 1 ELSE 0 END AS BIT) as IsManager

        FROM EmployeesWithManager

    </DefiningQuery>

</EntitySet>

<EntityType Name="EmployeeWithManagerStatus">

    <Key>

        <PropertyRef Name="EmployeeId" />

    </Key>

    <Property Name="EmployeeId" Type="int" Nullable="false" />

    <Property Name="IsManager" Type="bit" />

</EntityType>

When I do it, I just need to add a Manager entity type, add inheritance between it and Employee and then, map Manager entity type with my ssdl view. Then, I just need to add a condition on Manager entity type: “IsManager = true”.

And it’s all. Cool isn’t it?

Posted by Matthieu MEZIL | 2 comment(s)
Filed under: , ,