Living in the Tech Avalanche Generation

A practitioner’s introspective on technology

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 so far

  1. Steve February 22nd, 2010 2:17 pm

    Great series so far. A few items I’d like to see you address in your series at some point:

    1. you mention context. Examples of context if ’silverlight’ or ‘web’ or ‘behind wcf’. In other words, how are you handeling the EF context.

    I’m from a NHibernate world, and that is always a part of the discussion around the unit of work, if it’s a ‘per session’ or ‘conversation’ and what to do if it’s behind a WCF call, etc…

    Thanks again

    Steve

    [Reply]

  2. Steve February 22nd, 2010 2:19 pm

    I should add to above, more specifically, I see the ‘query’ aspects - do you plan on addressing the update/create aspects?

    My typical setup is to map to dto’s through service calls, and then sending these dto’s back to the service for update/create so with EF I’m dealing with how to handle the context, optimistic concurrency, etc…

    [Reply]

  3. Simon Segal February 22nd, 2010 10:09 pm

    Steve

    Glad your enjoying the series so far. I will address the points you have raised in the next post(s) and in the meantime I suggest it might be worth looking over what Udi says about ‘Realistic Concurrency’ here:

    http://www.udidahan.com/2007/01/22/realistic-concurrency/

    It’s something that has always been consistent with the way I generally work.

    With regard to addressing update / create aspects, do you mean something other than updates that would be persisted as a result of calling the behavior attached to the role itself? Ala:

    product.DiscountProductForRunOut();

    This method will likely do something like reduce a products UnitPrice for arguments sake. Does that address the update part of your question?

    Thanks for raising these questions and as I said I will address them in the next post(s).

    [Reply]

  4. Rad February 24th, 2010 4:49 pm

    Great post Simon. I was watching a while back Udi’s talk at QCon 2008 and I was waiting for this approach to be implemented elsewhere. He mentioned is his talk, that his explicit roles for validation, fetching and other cross cutting concerns are implemented in NServiceBus projects (except validation).
    I am working with an architect on introducing an abstraction layer that will allow both EF4 and NHibernate ORMs to be used together. This architect has a lot of NHibernate experience and I (senior developer) am trying to gather latest EF4 DDD related resources and help him shape the framework that is to be used for all types of clients: in ASP.NET/MVC, WCF, WPF, Silverlight, Console etc.
    It would be great to have NFetchSpec as part of a robust DDD framework for both EF4 and NHibernate where DDD concepts like UoW, Repository, PI, POCO, Aggregate Root, Value Objects, IoC/DI, TDD/BDD etc would be incorporated in a robust reusable framework with examples in ASP.NET/MVC, WCF and other clients.
    I would appreciate if you would provide a list with some recent resources that go well with your DDD with EF4 and NHibernate and NFetchSpec.
    Do you see T4 templates being used as the basis for generating some of the code for fetch strategies. I follow Matthieu MEZIL articles and his is using T4 templates a lot with EF4 design surface.

    I eagerly expect your next post and other related posts.

    Thank you,
    Rad

    [Reply]

  5. Simon Segal February 24th, 2010 10:03 pm

    Rad

    T4 is certainly on the radar for me when it comes to EF4 but I simply haven’t factored it into my thinking too deeply at this point and therefore cant elaborate on what exactly it’s role is (no pun intended).

    Regarding your request for a list of resources, are you referring to tooling specifically? If so, then I haven’t used anything specific at this juncture and the NFetchSpec proof of concept will probably be re-factored (fairly significantly) from proof of concept to production, once EF and Code Only are both in play. Let me make one remark on this however; I do plan to proof of concept, a role based approach that is intrinsically woven into Presentation Patterns such as MVP / MVC.

    Does that answer your question?

    Thanks,

    Simon

    [Reply]

  6. Rad February 24th, 2010 11:13 pm

    By list of resources I meant some real world, open source projects that follows DDD best practices using .NET 4.0 features particularly EF 4.0 as well as NHibernate the latest version (2.0+). I am aware of DDD books and I have read some of them. I also watched some videos on this topic. Basically I want to know who are the major DDD .NET 4.0 people who actively develop code similar to yours that would either be DDD oriented code or provide pluggable libraries as yours or anything that would be infrastructure kind of code.
    Will you at some point publish this as an open source project somewhere?

    Are you available for small tutoring sessions on DDD development using .NET 4.0 (20+ hours)? If yes, please send me details on my private email.

    Thanks,
    Rad

    [Reply]

  7. Simon Segal February 25th, 2010 8:00 pm

    Rad

    Apart from Udi and my colleagues I am not aware of any others who might be developing code in this role based approach. It is highly likely that after EF4 and the Code Only features have been release that I will publish the code for public consumption. Whether or not that includes turning it in to a true open source project I couldn’t really say right now.

    Thanks

    Simon

    [Reply]

  8. [...] 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 [...]

  9. [...] Helping the Entity Framework Play it’s <Role> Part 3.0 (posted) [...]

  10. [...] Helping the Entity Framework Play it’s <ROLE> Part 3.0 [...]

Leave a reply

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