Archive

Archive for December 9th, 2009

NServiceBus - Entity Framework Saga Persister

December 9th, 2009 Simon Segal 7 comments

In my last post I demonstrated a LINQ To SQL Saga Persister for NServiceBus and promised to follow it up with a similar dose of medicine - an Entity Framework Persister, specifically demonstrated for Version 1.0 of the Entity Framework.

Obviously after two previous posts on the LINQ To SQL Persister I am not going to cover the same ground in terms of explaining the various pieces of the puzzle, rather what I would like to do is simply point out some of the differences in the two implementations and note my observations.

As we did previously, let’s start out with the configuration code that uses the Entity Framework Persister.

static void Main()
{
    LogManager.GetLogger("hello").Debug("Order Started.");

    try
    {
        const string dbConnectionString =
          "metadata=..\\..\\Mapping\\OrderSagaDataModel.csdl|..\\..\\Mapping\\"+
          "OrderSagaDataModel.ssdl|..\\..\\Mapping\\OrderSagaDataModel."+
          "msl;provider=System.Data.SqlClient;provider connection "+
          "string=\"Data Source=BOOMER\\BOOM09;Initial Catalog=Sagas;"+
          "Integrated Security=True;MultipleActiveResultSets=True\"";

        var sessionFactory = EntityFrameworkSessionFactory
                             .Configure(dbConnectionString,
                                typeof(OrderSagaData), "OrderSagaDataLines");

        NServiceBus.Configure.With()
            .CastleWindsorBuilder(
                (cfg =>
                {
                    cfg.ConfigureComponent<OrderSagaFinder>
                        (ComponentCallModelEnum.Singlecall)
                        .SessionFactory = sessionFactory;
                }))
            .XmlSerializer()
            .MsmqTransport()
                .IsTransactional(true)
                .PurgeOnStartup(true)
                .IsolationLevel(IsolationLevel.RepeatableRead)
            .DbSubscriptionStorage()
                .Table("Subscriptions")
                .SubscriberEndpointColumnName("SubscriberEndpoint")
                .MessageTypeColumnName("MessageType")
            .Sagas()
            .EntityFrameworkSagaPersister<OrderSagaData>(sessionFactory)
            .UnicastBus()
                .ImpersonateSender(false)
                .LoadMessageHandlers(
                    First<GridInterceptingMessageHandler>
                        .Then<SagaMessageHandler>()
                 )
            .CreateBus()
            .Start();
    }
    catch (Exception e)
    {
        LogManager.GetLogger("hello").Fatal("Exiting", e);
    }
    Console.Read();
}

 

Kissing Cousins?

So what are the main differences between the LINQ To SQL and Entity Framework Persisters?

The Session Factory

  • Manages an ObjectContext vs. DataContext
  • Manages a string for the ObjectQuery<T>.Includes() argument to achieve eager fetching of the entire Saga’s state graph.
  • The Connection string includes the details and path to the mapping files.
  • No TextWriter for logging.
  • Requires to know the Type of Saga Entity to effect reading the mapping files meta data to ascertain the Default Container Name for the ObjectContext.

The Saga Finder

public OrderSagaData FindBy(string purchaseOrderNumber, Guid partnerId)
{
    SessionFactory.GetSession().DefaultContainerName = "SagasEntities";
    var sagaData = SessionFactory.GetSession()
        .CreateQuery<OrderSagaData>("[OrderSagaData]")
        .Where(o => o.PurchaseOrderNumber == purchaseOrderNumber &&
                    o.PartnerId == partnerId);

    foreach(var include in SessionFactory.IncludeInFetching)
    {
        sagaData = ((ObjectQuery<OrderSagaData>) sagaData).Include(include);
    }

    return sagaData.FirstOrDefault();
}

It’s almost identical to the LINQ To SQL finder with the following differences:

  • Eager Fetching via the .Include method.
  • Requires the DefaultContainerName which is available from the MetaDataWorkspace of an EntityConnection – this is specified in the mapping files.

Other Stuff

  • Isolation Level Defaults in the Entity Framework were by default Serializable and LINQ To SQL varied according the the operation in use. Setting the isolation level when configuring the bus settles this for both Persisters.
  • LINQ To SQL Persister requires cascade delete to be set in the database to clean up the entire Saga.
  • Entity Framework Version 1.0 of course does not support POCO and LINQ To SQL for the most part does.
  • I don’t like the way Entity Framework forces me to have persistence code bleed into my Saga Entities – notice how the OrderSagaData class made its way from the OrderService to the OrderService.Persistence project so it could sit alongside the Entity Data Model.

The Last Word

Just as with the LINQ To SQL Persister, this one needs to be merged via a batch file in the build folder and the subsequent single DLL is all you will need to use the this Persister.

image

I have made the entire code for the Entity Framework Saga Persister available from this link. I shall finish off the series by following up shortly by demonstrating how to use the Persister with the next version of the Entity Framework (4.0), which thankfully does support POCO.

Share/Save/Bookmark

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