TPH, how to change the entity type (ie: to change the column mapped on conditions)?

Imagine the following scenario:

I use Northwind. I add on Employee table two columns: OutDate and OutType.

In my edmx, I have an abstract entity type Employee, inherited by EmployeeInActivity (OutDate Is Null) and OutEmployee (OutDate Is Not Null). OutEmployee is inherited by FiredEmployee (OutType = F).

Ok, great, with TPH, EF supports this scenario.

But I have a problem: in my business logic, an employee (in activity) can be fired.

To be able to do this, we can imagine using a SSDL function to change the OutDate and OutType column and then reloading the employee. Yes but I want to do the persistance during my SaveChanges and not before.

Moreover, the customer who asks me how to realize this scenario doesn’t want to use a SSDL function.

I advise him to have two models but he doesn’t want to manage two models in his services and he wants to call SaveChanges on only one ObjectContext.

So what is my solution:

I create two models: TPHModel and FlatModel.

Flat model CSDL is just this:

<EntityContainer Name=FlatEntities>

    <EntitySet Name=EmployeeSet EntityType=NorthwindModel.Employee a:GetterAccess=Internal xmlns:a=http://schemas.microsoft.com/ado/2006/04/codegeneration />

</EntityContainer>

<EntityType Name=Employee a:TypeAccess=Internal xmlns:a=http://schemas.microsoft.com/ado/2006/04/codegeneration>

    <Key>

        <PropertyRef Name=EmployeeID />

    </Key>

    <Property Name=EmployeeID Type=Int32 Nullable=false />

    <Property Name=OutDate Type=DateTime />

    <Property Name=OutType Type=String MaxLength=1 Unicode=false FixedLength=true />

</EntityType>


Then, I add a boolean property on FiredEmployee:

partial class FiredEmployee

{

    internal bool FiredTemporary { get; set; }

}


Now, in TPHModel ObjectContext class, I add a method Fire:

public FiredEmployee Fire(EmployeeInActivity employee)

{

    var propertiesModified = ObjectStateManager.GetObjectStateEntry(employee).GetModifiedProperties().ToList();

    var entityKey = employee.EntityKey;

    var entityState = employee.EntityState;

    Detach(employee);

    var firedEmployee = new FiredEmployee { Address = employee.Address, BirthDate = employee.BirthDate, City = employee.City, Country = employee.Country, EmployeeID = employee.EmployeeID, Extension = employee.Extension, FiredTemporary = entityState != EntityState.Added, FirstName = employee.FirstName, HireDate = employee.HireDate, HomePhone = employee.HomePhone, LastName = employee.LastName, Notes = employee.Notes, OutDate = DateTime.Now, Photo = employee.Photo, PhotoPath = employee.PhotoPath, PostalCode = employee.PostalCode, Region = employee.Region, Title = employee.Title, TitleOfCourtesy = employee.TitleOfCourtesy };

    firedEmployee.ManagerReference.EntityKey = employee.ManagerReference.EntityKey;

 

    switch (entityState)

    {

        case EntityState.Added:

            AddToEmployeeSet(firedEmployee);

            break;

        case EntityState.Unchanged:

            AttachTo(entityKey.EntitySetName, firedEmployee);

            break;

        case EntityState.Modified:

            AttachTo(entityKey.EntitySetName, firedEmployee);

            var objectStateEntry = ObjectStateManager.GetObjectStateEntry(firedEmployee);

            objectStateEntry.SetModified();

            foreach (var property in propertiesModified)

                objectStateEntry.SetModifiedProperty(property);

            break;

    }

    return firedEmployee;

}


Then, I use partial method OnContextCreated to add delegate to SavingChanges event and to automatically use FlatModel.

partial void OnContextCreated()

{

    SavingChanges +=

        (sender, e) =>

        {

            using (var context = new FlatEntities())

            {

                foreach (var firedEmployee in ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified | EntityState.Unchanged).Select(ose => ose.Entity).OfType<FiredEmployee>().Where(em => em.FiredTemporary).ToList())

                {

                    var employee = context.EmployeeSet.FirstOrDefault(em => em.EmployeeID == firedEmployee.EmployeeID);

                    if (employee != null) // the employee hasn’t been deleted in DB

                    {

                        employee.OutDate = firedEmployee.OutDate;

                        employee.OutType = “F”;

                    }

                    firedEmployee.FiredTemporary = false;

                }

                context.SaveChanges();

            }

        };

}


And here we are. The use of FlatModel is now transparent for TPHModel users. Moreover, I set all the FlatModel generated classes internal to impose the use of TPHModel.

This entry was posted in 7671, 7674, 7675. Bookmark the permalink.

One Response to TPH, how to change the entity type (ie: to change the column mapped on conditions)?

  1. ... says:

    Sehr wertvolle Informationen! Empfehlen!

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>