Living in the Tech Avalanche Generation

A practitioner’s introspective on technology

Archive for the 'Entity Framework' Category

NFetchSpec Code – Entity Framework Repository, Fetching Strategies, Specifications, Code Only, Mapping, POCO and Making Roles Explicit.

I have made the code from the NFetchSpec spiking / proof of concept available here. Be aware that it requires .NET Framework 4.0, Entity Framework 4.0 and the Feature CTP  versions 2 or 3. I won’t be revising this code until the official release of version 4.0 and it will likely get some reworking. In the meantime if you want to understand the ideas in parts 1, 2, 3, and 4 of Helping the Entity Framework Play it’s <ROLE> the you can feel free to pull the code apart and do with it what you will. Until then.

Share/Save/Bookmark

No comments

Helping Entity Framework v4.0 play it’s <ROLE> – Part 4.0

Before diving in you might like to read parts 1, 2 and 3 if you have come to this post out of sync. Also if you are not familiar with some of the newer aspects of the Code Only CTP for Entity Framework it might be worth looking here on the ADO.Net Team blog.

The Service Layer

nfetchspec_serviceLayer_2

I want to point out that this approach in development is not restricted to a technology or architecture per`se and in discussing how it fits with a ‘Service Layer’ is equally valid for a Layered Request / Response style Architecture and asynchronous messaging systems with very well defined business components.

Let’s start with a run of the mill WCF approach – I have purposely elided the interface contract for the sake of brevity.

[OperationContract(IsOneWay=true)]
[OperationBehaviour(TranactionScopeRequired=true,
                    TransactionScopeAutoComplete=false)]
public void DiscountRunOutProducts()
{
    IList<IRunOutDiscountForProducts> products = null;

    var compParams = new Dictionary<string, string>();
    compParams.Add(_conParamName, _connectionStringOnly);

    var repo = FetchSpec
                    .Configure
                    .With(new IoC())
                    .AndBuildSession<IRunOutDiscountForProducts>(compParams)
                    .ForRepository();

    using (var scope = new TransactionScope(TransactionScopeOption.Required,
        new TransactionOptions()
        {
            IsolationLevel =
                SessionBuilder<IRunOutDiscountForProducts>.ScopeIsolation()
        }))
    {
        products = repo.Get<IRunOutDiscountForProducts>();

        foreach (var product in products)
        {
            product.DiscountProductForRunOut();
        }

        scope.Complete();
    }
}

Every expression of intent here is explicit, from the name of the service operation, all the way to the application of the ROLE to the ORM. From an ongoing maintenance perspective, when any of the given actors need to be varied through the life cycle of the software, this can be done independently. For example, we don’t have to revisit our service layers code when we profile the system and find that the fetching strategy should be eager fetching for a given scenario, instead we can build a new version of the existing Fetching Strategy and drop it in. This kind of flexibility ripples through the system, with the same said for Specifications, Message Handlers, so on and so forth. If of course you wanted to change the code to accept a Specification ‘in line’ rather than have it deployed and dynamically resolved, then you could probably have just implemented an overloaded version of the Service operation to begin with and pass in the parameters required to build those pieces at runtime. The example is a one way operation but you could easily be returning values and accepting parameters (think DTO’s) in a request / response scenario. Bear in mind these DTO’s are not being mapped and subsequently attached, they are either parameters for the ORM or return property buckets for binding or mapping to a presentation model.

For a point of difference, let’s take a look at how this might work if our Service Layer was implemented on top of the popular Open Source Framework NServiceBus.

public class DiscountRunOutProductsMessage: IMessage
{
    public string DiscountedRegion{ get; set; }
    public decimal UnitPrice{ get; set; }
}

public class DiscountRunOutProducts :
             IMessageHandler<DiscountRunOutProductsMessage>
{
    public void Handle(DiscountRunOutProductsMessage message)
    {
        IList<IRunOutDiscountForProducts> products = null;

        var compParams = new Dictionary<string, string>();
        compParams.Add(_conParamName, _connectionStringOnly);

        var repo = FetchSpec.Configure
                            .With(new IoC())
                            .AndBuildSession<IRunOutDiscountForProducts>
                                (compParams)
                            .ForRepository();

        using (var scope = new TransactionScope(TransactionScopeOption.Required,
            new TransactionOptions()
            {
                IsolationLevel =
                    SessionBuilder<IRunOutDiscountForProducts>.ScopeIsolation()
            }))
        {
            var spec = new Specification<IRunOutDiscountForProducts>
                            (p => p.UnitPrice < message.UnitPrice &&
                             p.Supplier.Region = message.DiscountedRegion);

            products = repo.Get<IRunOutDiscountForProducts>(spec);

            foreach (var product in products)
            {
                product.DiscountProductForRunOut();
            }

            scope.Complete();
        }
    }
}

Something worth pointing out in our NServiceBus example is that it too is quite explicit. The message handler, handles messages expressly for the purpose of discounting products to be ‘run out’, exemplifying how the pattern is becoming more entrenched in our style of working. Once again, if we don’t like anything about the way the handler is doing it’s work, we can throw it away in preference for a new implementation.

Not to be left out

Steve left a comment and asked me not to forgo examples that dealt with inserting and updating data, Steve also wanted to know about how I go about dealing with the Unit of Work and the ‘per session’ vs. ‘conversation’ and the differences that spring up and also when working with WCF and dealing with optimistic concurrency. Fortunately this part in the series takes aim squarely at how to go about using Entity Framework in concert with Roles from the Service Layer and we tackle those questions by necessity.

Listing 1.0 – Inserts

public void add_a_product()
{
    var compParams = new Dictionary<string, string>();
    compParams.Add(_conParamName, _connectionStringOnly);

    var repo = FetchSpec.Configure
                        .With(new StubIoc() {
                            ScannedAssemblyPath = _scannedAssemblyPath })
                        .AndBuildSession<ICustomerToAddOrder>(compParams)
                        .ForRepository();

    var spec =
        new Specification<ICustomerToAddOrder>(c => c.CustomerID == “ALFKI”);
    var config = new IQueryConfigurable[] { spec };

    using (var scope = new TransactionScope(TransactionScopeOption.Required,
        new TransactionOptions()
        {
                IsolationLevel =
                    SessionBuilder<ICustomerToAddOrder>.ScopeIsolation()
        }))
    {
        dynamic customer = repo.Get<ICustomerToAddOrder>(config).First();

        var order = repo.IocContainer
                        .ConvertFromRoleToImpl<IOrderToAddToCustomer>();
        order.CustomerID = customer.CustomerID;
        order.Customer = customer;

        customer.AddOrder(order);

        repo.Save();

        scope.Complete();
    }
}

Not entirely in the spirit of things

Notice the use of the dynamic keyword above (suggested by Mark) Let me explain. Due the Entity Frameworks hard and fast rule of only allowing EntityConfiguration<T> to specify a concrete class as it’s generic argument, the flow through effects have rippled out, with the customer variable and the order variable. The problem is that the IoC container can’t know ahead of time what the implementation type for the interface might be and order.Customer property is forced to use a concrete type (not interface) to satisfy the Entity Framework for mapping purposes enforced by EntityConfiguration<T>. After speaking to David DeWinter (a Tester on the EF Team who responded to a tweet I made), he suggested that it was likely implemented this way to satisfy ObjectSet<T> which also constrains to a concrete implementation (makes sense).

Ultimately the dynamic keyword will get us over the line with respect to this one final hurdle, however my gut says that this work around isn’t entirely in the spirit of NFetchSpec and smacks a little of my old enemy the VARIANT, but I’m prepared to make this one allowance to get me working in a way that suits my needs. I can still read the code somewhat clearly because even when I’m using dynamic for assignment, I can reason about the type based on the interface specified to the repository and the IoC container. I would love to think that this is something that the Entity Framework team might address but I guess I will just have to wait and see.

Under the covers

What code did I need to write to support this ‘adding a product to a customer’ scenario? First up I needed the role itself, then the entities, the mapping classes, the mapping role class and I also needed to write an implementation for IProvideIsolationLevelFor. The Specification I chose to write inline because the parameter value would be provided at run time. Here is the all of it:

The Roles

public interface ICustomerToAddOrder
{
    void AddOrder(IOrderToAddToCustomer order);
    string CustomerID { get; set; }
    ICollection<OrderToAddToCustomer> Orders { get; set; }
}
public interface IOrderToAddToCustomer
{
    string CustomerID { get; set; }
    CustomerToAddOrder Customer { get; set; }
    int OrderID { get; set; }
}

The Entities

public class CustomerToAddOrder : IEntity, ICustomerToAddOrder
{
    public string CustomerID { get; set; }
    public ICollection<OrderToAddToCustomer> Orders { get; set; }

    public void AddOrder(IOrderToAddToCustomer order)
    {
        if (Orders == null)
            Orders = new List<OrderToAddToCustomer>();

        Orders.Add(order as OrderToAddToCustomer);
    }
}

public class OrderToAddToCustomer : IEntity, IOrderToAddToCustomer
{
    public string CustomerID
    {
        get;
        set;
    }

    public CustomerToAddOrder Customer
    {
        get;
        set;
    }

    public int OrderID
    {
        get;
        set;
    }
}

The Mapping Classes

public class CustomerToAddOrderMapping : Mapping<CustomerToAddOrder>
{
    public CustomerToAddOrderMapping(string objectSetName)
        : base(objectSetName)
    {
        Property(c => c.CustomerID).HasMaxLength(5).IsRequired();
        HasKey(c => c.CustomerID);
        Relationship(c => c.Orders).IsOptional();
    }
}

public class OrderToAddToCustomerMapping : Mapping<OrderToAddToCustomer>
{
    public OrderToAddToCustomerMapping(string objectSetName)
        : base(objectSetName)
    {
        Property(o => o.OrderID).IsIdentity();
        Property(o => o.CustomerID).HasMaxLength(5).IsRequired();
        HasKey(o => o.OrderID);
        Relationship(o => o.Customer)
            .FromProperty(c => c.Orders)
            .HasConstraint((o, c) => o.CustomerID == c.CustomerID);
    }
}

The Mapping Role

public class CustomerToAddOrderMappingRole :
        IMappingRole<ICustomerToAddOrder>
{
    private IMapping[] _mappings;

    public CustomerToAddOrderMappingRole()
    {
        _mappings = new IMapping[]
        {
            new CustomerToAddOrderMapping(“Customers”),
            new OrderToAddToCustomerMapping(“Orders”)
        };
    }

    public IMapping[] Mappings
    {
        get { return _mappings; }
    }
}

The Transaction Isolation Class

public class AddOrderToCustomerIsolation :
        IProvideIsolationLevelFor<ICustomerToAddOrder>
{
    public IsolationLevel GetScopeIsolationForRole
    {
        get { return IsolationLevel.ReadCommitted; }
    }
}

Piggy in the middle

The issue of managing a short or extended session with an ObjectContext is something that I think a lot of people struggle with. Personally I have never had such an issue because they way I have always worked always involved fetching the most current version of the data I required to have my domain model work against it. This way of working has stayed with me through time immemorial, even in my dark old days of using an anaemic domain model.  To be clear I do not pass DTO’s around and reattach them to the ObjectContext, rather refreshing the data and letting the domain model’s business logic take care of the rest.

In Conclusion

My initial planning for the series was that would be take up four posts and therefore it is expected that this may be the last in the series. However, given that Entity Framework 4.0 is still not at RTM and Code Only is still in CTP then it’s possible I may revisit it with a further post sometime in the future.

Share/Save/Bookmark

2 comments

Helping Entity Framework v4.0 play it’s <ROLE> – Part 3.0

In part 1.0 we laid the foundation and part 2.0 unravelled some of the internal details of how NFetchSpec (the libraries that are subject of this discussion) goes about enlisting the benefits that can be derived by using explicit roles in your system by collapsing a swathe of Entity Framework functionality into a somewhat compact approach to dealing with a Domain Model. First a quick recap of the underpinnings.

Figure 1.0

ef_roles_part3_nfetch_in_action_usage_quadrant

Code from Figure 1.0

public void resolves_everything_from_container()
{
    IList<IRunOutDiscountForProducts> products = null;

    var compParams = new Dictionary<string, string>();
    compParams.Add(_conParamName, _connectionStringOnly);

    var repo = FetchSpec.Configure
                            .With(new IoC())
                            .AndBuildSession<IRunOutDiscountForProducts>
                                (compParams)
                            .ForRepository();

    using (var scope = new TransactionScope(TransactionScopeOption.Required,
        new TransactionOptions()
        {
            IsolationLevel =
                SessionBuilder<IRunOutDiscountForProducts>.ScopeIsolation()
        }))
    {
        products = repo.Get<IRunOutDiscountForProducts>();
        foreach (var product in products)
        {
            product.DiscountProductForRunOut();
        }

        scope.Complete();
    }
}

Figure 1.0 demonstrates how all the moving parts in the developer experience are put to use when using the NFetchSpec libraries to query and make changes to a persisted Domain Model. Let’s now examine in detail all the steps that make up the entire developer experience of composing all the discrete pieces needed in addressing the role of IRunOutDiscountForProducts (in our fictitious system).

The Development Scenario

image The first outlined section in figure 1.0 demonstrates the building of an appropriate ObjectContext for the Repository that is instantiated by the fluent interface. From the developer experience perspective, nothing special needs to be done outside of specifying the role to the fluent interface in this manner.

image The second outlined section in figure 1.0 highlights how the role is leveraged to dynamically have NFetchSpec chose the ‘right’ isolation level. Being explicit in this way we can achieve a high degree of separation of concerns with respect to transaction isolation and thus exercise our control over the concurrency and throughput in the database on a role by role basis. In the example, we are discounting products earmarked for a run out sale, consequently changing their unit price, therefore we might like to choose a higher isolation level for that particular operation, depending on the volatility of the data that your dealing with and the normalization or lack thereof in your database schema. For the developer this means creating a concrete implementation of the interface IProvideIsolationLevelFor<T>, to support this automagic behaviour.

public interface IProvideIsolationLevelFor<TRole>
{
    IsolationLevel GetScopeIsolationForRole { get;}
}

public class RunoutDiscountIsolation :
             IProvideIsolationLevelFor<IRunOutDiscountForProducts>
{
    public IsolationLevel GetScopeIsolationForRole
    {
        get { return IsolationLevel.ReadCommitted; }
    }
}

That’s it as far as getting your transaction set for the right Isolation Level within the scope of work for a given role. If we decide at some point that this Isolation Level is not the best fit, we can replace it with a new implementation.

image The Third outlined section in figure 1.0 highlights the use of the Repository to retrieve the Entities that we are interested in. This is where the NFetchSpec machinery goes to work to resolve all the purpose built artefacts that have been implemented for the given role, such as any applicative Fetching Strategy, Specification, Entity and Mapping.

The Loaded Fetching Strategy

public class ProductRunOutFetchingStrategy :
             FetchingStrategy<IRunOutDiscountForProducts>
{
    public ProductRunOutFetchingStrategy()
        : base(false)
    {
        var production_intentions =
            EagerFetchingIntention
            .CreateInstance<IRunOutDiscountForProducts,
                            ISupplier>(p => p.Supplier);

        this.AddIntentions(new IEagerFetchingIntention[]
        {
            production_intentions
        });
    }
}

The intent of the developer in building this Fetching Strategy is to load Products eagerly with their reference Supplier.

The Loaded Specification

public class RunOutProductDiscountSpecification :
             Specification<IRunOutDiscountForProducts>
{
    public RunOutProductDiscountSpecification()
        : base(p => p.UnitPrice > 5M) { }
}

The Specification is really quite simple and self evident – setting up the applicative Expression that will ultimately be translated into a ‘where’ predicate.

The Loaded Mapping

First up we need to map our Entities and that requires a Mapping<T>.

public class ProductMapping : Mapping<Product>
{
    public ProductMapping(string objectSetName) : base(objectSetName)
    {
        Property(p => p.ProductID).IsIdentity();
        Property(p => p.ProductName).HasMaxLength(50).IsRequired();
        Property(p => p.UnitPrice).Precision = 19;
        Property(p => p.UnitPrice).Scale = 4;
        Relationship(p => p.Supplier).IsOptional();
        Relationship(p => p.Supplier).IsOptional().FromProperty(s => s.Products);
    }
}

And because our example has a reference Entity in the root we require the following Supplier Entity Mapping also.

public class SupplierMapping : Mapping<Supplier>
{
    public SupplierMapping(string objectSetName)
        : base(objectSetName)
    {
        Property(s =>  s.SupplierID).IsIdentity();
        Property(s => s.CompanyName).HasMaxLength(40).IsRequired();
        Property(s => s.Region).HasMaxLength(15);
        Relationship(s => s.Products).IsOptional().FromProperty(p => p.Supplier);
    }
}

The last requirement of the developer with respect to mapping is to create a MappingRole<T>, the purpose of which is to indicate to the infrastructure which mappings are applicable to the Role.

public class RunOutDiscountMappingRole :
             IMappingRole<IRunOutDiscountForProducts>
{
    private IMapping[] _mappings;

    public RunOutDiscountMappingRole()
    {
        _mappings = new IMapping[]
                    {
                        new ProductMapping("Products"),
                        new SupplierMapping("Suppliers")
                    };
    }

    public IMapping[] Mappings
    {
        get { return _mappings; }
    }
}

image The fourth and final outlined section in figure 1.0 is really the essence of what is going on here, it is entirely the reason this code is being invoked – it is the purpose (discounting products) of the system for this role. When developing in this way we need to specify the behaviour of the Role through the stable abstraction of it’s interface, and implement that behaviour in a concrete Entity.

public interface IRunOutDiscountForProducts : IProductData
{
    void DiscountProductForRunOut();
}

The Bang and Crash

So with all that done, we need to deploy our “Role inspired” artefacts to the executing processes assemblies folder and when it’s all said and done the code from Figure 1.0 executes, enlisting the service of NFetchSpec in applying the combined intent and customised behaviours described in all of the deployed pieces.

From the database perspective, SQL Profiler tells us that our intent for this ROLE has been met and the system has behaved precisely as expected.

SELECT
    [Extent1].[ProductID] AS [ProductID],
    [Extent1].[ProductName] AS [ProductName],
    [Extent1].[UnitPrice] AS [UnitPrice],
    [Extent2].[CompanyName] AS [CompanyName],
    [Extent2].[Region] AS [Region],
    [Extent2].[SupplierID] AS [SupplierID]
FROM
    [dbo].[Products] AS [Extent1]
LEFT OUTER JOIN
    [dbo].[Suppliers] AS [Extent2]
ON
    [Extent1].[SupplierID] = [Extent2].[SupplierID]
WHERE
    [Extent1].[UnitPrice] > cast(5 as decimal(18))

UPDATE: 14th March 2010

The query above was pasted in error previously. The current text for the TSQL here is correct as of the date listed here in this update.

We can see that Fetching Strategy and Specification have been successful in shaping the TSQL sent to the server to retrieve the data. Below we can also see the effect of applying the business rules through our Domain Model.

exec sp_executesql N‘update [dbo].[Products]
set [UnitPrice] = @0
where ([ProductID] = @1)
‘,N‘@0 decimal(19,4),@1 int’,@0=3.2960,@1=1

Just one more thing

Part 4.0 will be the final part in the series and will focus on how this approach blends into a service layer in a Service Oriented Architecture and also make the code for NFetchSpec available for download.

Share/Save/Bookmark

10 comments

Next Page »

Creative Commons Attribution-ShareAlike 2.5 Australia
Creative Commons Attribution-ShareAlike 2.5 Australia