Wednesday 18 December 2013

Unit Of Work Abstraction For NHibernate or Entity Framework C# Example

Unit Of Work is another great pattern that every software engineer needs to have in his tool box. So what's a purpose of Unit Of Work?

 “Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems.” – Patterns of enterprise application architecture, Martin Fowler

Sounds like a lot of work, managing object state, relationships, concurrency and transactions can be cumbersome. However we are in luck, modern ORM's like NHibernate and Entity Framework support implicit Unit Of Work. In NHibernate it’s part of the ISession and in Entity Framework it’s part of the Context.

NHibernate
using(ISession session = sessionFactory.OpenSession()) 
using(ITransaction tx = session.BeginTransaction()) 
{ 
        Customer customer = new Customer { FirstName = "Peter" };
        session.Save(customer);
        tx.Commit(); 
} 

Entity Framework
using (SomeContext context = new SomeContext())
{
    Customer customer = new Customer { FirstName = "Peter" };
    context.Customer.Add(customer);
    context.SaveChanges();
}

ORM's have built in Unit Of Work, ideally these need to be abstracted away. Abstraction will allow us to move to a different ORM platform in the future and yield cleaner architecture. Lets take a look at the abstracted example:
Customer customer = new Customer { FirstName = "Peter"};
this.repositoryCustomer.Add(customer);
this.unitOfWork.Commit();

When it comes to working with databases it's important to read and write within a transaction scope. This ensures that there are no data inconsistencies, performance issues and second level cache is used. Please read this article for more information. So we need to open a transaction, but when do we do it?
  1. Open transaction manually in the service layer - Default Unit Of Work interface doesn't support .BeginTransaction, I don't believe it should be controlled by the user. Although this is a clean option.
  2. Open transaction on the page context creation - This feels dirty, each time some one hits your website your ORM will open a connection and begin a transaction. Not great for performance and is dangerous. 
  3. Open transaction in the repository on the first read or write - Best option, it's automatic, and the only down side is that there will be transaction null check each time (singleton). But you get cleaner service implementation.
In this article I am going to be focusing on point 3.

NHibernate Abstraction

Configuration Singleton
public static class NHConfigurationSingleton
    {
        private static Configuration configuration = null;
        private static object lockObj = new object();

        public static Configuration Configuration
        {
            get
            {
                lock (lockObj)
                {
                    if (configuration == null)
                    {
                        string[] resourceNames;
                        string nHResource = string.Empty;
                        Assembly[] asmArray = AppDomain.CurrentDomain.GetAssemblies();

                        foreach (Assembly asm in asmArray)
                        {
                            resourceNames = asm.GetManifestResourceNames();
                            nHResource = resourceNames.FirstOrDefault(x => x.ToLower().Contains("hibernate.config"));
                            if (!string.IsNullOrEmpty(nHResource))
                            {
                                using (Stream resxStream = asm.GetManifestResourceStream(nHResource))
                                {
                                    configuration = new Configuration();
                                    configuration.Configure(new XmlTextReader(resxStream));
                                }
                            }
                        }
                    }
                }

                return configuration;
            }
        }
    }

Session Factory Singleton
    public static class NHSessionFactorySingleton
    {
        private static ISessionFactory sessionFactory = null;
        private static object lockObj = new object();

        public static ISessionFactory SessionFactory
        {
            get
            {
                lock (lockObj)
                {
                    if (sessionFactory == null)
                    {
                        sessionFactory = NHConfigurationSingleton.Configuration.BuildSessionFactory();
                    }
                }

                return sessionFactory;
            }
        }
    }


Unit Of Work
    
public interface IUnitOfWork : IDisposable
    {
        void Commit();
        void Rollback();
    }

public class NHUnitOfWork : IUnitOfWork
    {
        private ISession session;
        private ITransaction transaction;
        public ISession Session { get { return this.session; } }

        public NHUnitOfWork() { }

        public void OpenSession()
        {
            if (this.session == null || !this.session.IsConnected)
            {
                if (this.session != null)
                    this.session.Dispose();

                this.session = NHSessionFactorySingleton.SessionFactory.OpenSession();
            }
        }

        public void BeginTransation(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
        {
            if (this.transaction == null || !this.transaction.IsActive)
            {
                if (this.transaction != null)
                    this.transaction.Dispose();

                this.transaction = this.session.BeginTransaction(isolationLevel);
            }
        }

        public void Commit()
        {
            try
            {
                this.transaction.Commit();
            }
            catch
            {
                this.transaction.Rollback();
                throw;
            }
        }

        public void Rollback()
        {
            this.transaction.Rollback();
        }

        public void Dispose()
        {
            if (this.transaction != null)
            {
                this.transaction.Dispose();
                this.transaction = null;
            }

            if (this.session != null)
            {
                this.session.Dispose();
                session = null;
            }
        }
    }

Repository
  
public class NHRepository<TEntity> : IRepoistory<TEntity>
        where TEntity : IDomainEntity
    {
        readonly NHUnitOfWork nhUnitOfWork;

        public NHRepository(NHUnitOfWork nhUnitOfWork)
        {
            this.nhUnitOfWork = nhUnitOfWork;
        }

        public void Add(TEntity entity)
        {
            this.nhUnitOfWork.OpenSession();
            this.nhUnitOfWork.BeginTransation();
            this.nhUnitOfWork.Session.Save(entity);
        }

        public void Remove(TEntity entity)
        {
            this.nhUnitOfWork.OpenSession();
            this.nhUnitOfWork.BeginTransation();
            this.nhUnitOfWork.Session.Delete(entity);
        }

        public void Update(TEntity entity)
        {
            this.nhUnitOfWork.OpenSession();
            this.nhUnitOfWork.BeginTransation();
            this.nhUnitOfWork.Session.Update(entity);
        }
        
        public IEnumerable<TEntity> GetAll()
        {
            this.nhUnitOfWork.OpenSession();
            this.nhUnitOfWork.BeginTransation();
            return this.nhUnitOfWork.Session.Query<TEntity>();
        }
    }

Entity Framework Abstraction

Unit Of Work
 
   public class EFUnitOfWork : IUnitOfWork
    {
        DbContext context;
        DbContextTransaction transaction;
        public DbContext Context { get { return this.context; } }

        public EFUnitOfWork() 
        { 
            this.context = new ECommerceEntities();
        }

        public void OpenTransaction(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
        {
            if (this.transaction == null)
            {
                if (this.transaction != null)
                    this.transaction.Dispose();

                this.transaction = this.context.Database.BeginTransaction(isolationLevel);
            }
        }

        public void Commit()
        {
            try
            {
                this.context.SaveChanges();
                this.transaction.Commit();
            }
            catch
            {
                this.transaction.Rollback();
                throw;
            }
        }

        public void Rollback()
        {
            this.transaction.Rollback();
        }

        public void Dispose()
        {
            if(this.transaction != null)
            {
                this.transaction.Dispose();
                this.transaction = null;
            }

            if(this.context != null)
            {
                this.context.Database.Connection.Close();
                this.context.Dispose();
                this.context = null;
            }
        }
    }

Repository
 
 public class EFRepository<TEntity> : IRepoistory<TEntity>
        where TEntity : class, IDomainEntity
    {
        readonly EFUnitOfWork unitOfWork;

        public EFRepository(EFUnitOfWork unitOfWork)
        {
            this.unitOfWork = unitOfWork;
        }

        public void Add(TEntity entity)
        {
            this.unitOfWork.OpenTransaction();
            this.unitOfWork.Context.Set<TEntity>().Add(entity);
        }

        public void Remove(TEntity entity)
        {
            this.unitOfWork.OpenTransaction();
            this.unitOfWork.Context.Set<TEntity>().Remove(entity);
        }

        public IEnumerable<TEntity> GetAll()
        {
            this.unitOfWork.OpenTransaction();
            return this.unitOfWork.Context.Set<TEntity>().ToList();
        }
    }

Example usage (see SQL trace output below)
    
public class CustomerService
    {
        readonly IUnitOfWork unitOfWork;
        readonly IRepoistory<Customer> repositoryCustomer;

        public CustomerService(IUnitOfWork unitOfWork, IRepoistory<Customer> repositoryCustomer)
        {
            this.unitOfWork = unitOfWork;
            this.repositoryCustomer = repositoryCustomer;
        }

  public CustomerDto Add(string firstName, string lastName)
        {
            int existingWithSameName = this.repositoryCustomer.GetAll()
                    .Count(customer => customer.FirstName == firstName && customer.LastName == lastName);

            if (existingWithSameName != 0)
                throw new Exception("User already exists with this name");

            Customer customerNew = new Customer(firstName, lastName);
            
            this.repositoryCustomer.Add(customerNew);
            this.unitOfWork.Commit();

            return new CustomerDto(customerNew.Id, customerNew.FirstName, customerNew.LastName);
        }

    }

NHibernate SQL Trace Output
To see exactly what's going on i've decided to go through SQL logs. It seems to behave correctly, it connects, starts a transaction, performs required operations, commits transaction and closes all connections.
2013-12-17 23:24:24.09 spid51      ODS Event: Login connection 51
2013-12-17 23:24:24.09 spid51      ODS Event: Remote_ods : Xact 0 ORS#: 1, connId: 0
2013-12-17 23:24:24.09 spid51      Xact BEGIN for Desc: 3300000001
2013-12-17 23:24:25.19 spid51      ODS Event: language_exec : Xact 3300000001 ORS#: 1, connId: 0
2013-12-17 23:24:25.19 spid51         Text:select customer0_.Id as Id0_, customer0_.FirstName as FirstName0_, customer0_.LastName as LastName0_ from ECommerce.dbo.Customer customer0_
2013-12-17 23:24:25.35 spid51      Parameter# 0: Name=,Flags=0,Xvt=231,MaxLen=166,Len=166,Pxvar Value=INSERT INTO ECommerce.dbo.Customer (FirstName, LastName, Id) VALUES (@p0, @p1, @p2)
2013-12-17 23:24:25.36 spid51      Parameter# 1: Name=,Flags=0,Xvt=231,MaxLen=116,Len=116,Pxvar Value=@p0 nvarchar(4000),@p1 nvarchar(4000),@p2 uniqueidentifier
2013-12-17 23:24:25.36 spid51      Parameter# 2: Name=@p0,Flags=0,Xvt=231,MaxLen=8000,Len=10,Pxvar Value=ZanNH
2013-12-17 23:24:25.36 spid51      Parameter# 3: Name=@p1,Flags=0,Xvt=231,MaxLen=8000,Len=14,Pxvar Value=70ba671
2013-12-17 23:24:25.36 spid51      Parameter# 4: Name=@p2,Flags=0,Xvt=36,MaxLen=16,Len=16,Pxvar Value=E5EF2FD7-532B-4CEA-80F4-8B095D1F983A
2013-12-17 23:24:25.36 spid51          IPC Name: sp_executesql
2013-12-17 23:24:25.36 spid51      ODS Event: execrpc : Xact 3300000001 ORS#: 1, connId: 0
2013-12-17 23:24:25.36 spid51      ODS Event: Remote_ods : Xact 3300000001 ORS#: 1, connId: 0
2013-12-17 23:24:25.36 spid51      Xact COMMIT for Desc: 3300000001
2013-12-17 23:24:27.71 Server      ODS Event: Exit spid 51
2013-12-17 23:24:27.71 spid51      ODS Event: Logout connection 51
2013-12-17 23:24:27.71 Server      ODS Event: Close conn 0 on spid 51

Entity Framework SQL Trace Output
2013-12-17 23:27:06.21 spid51      ODS Event: Login connection 51
2013-12-17 23:27:06.22 Server      Connection 0x00000001EE6433F0 connId:1, spid:51     New MARS Session 
2013-12-17 23:27:06.22 spid51      ODS Event: Remote_ods : Xact 0 ORS#: 1, connId: 1
2013-12-17 23:27:06.22 spid51      Xact BEGIN for Desc: 3300000001
2013-12-17 23:27:08.06 Server      Connection 0x00000001F2F317A0 connId:2, spid:51     New MARS Session 
2013-12-17 23:27:08.06 spid51      ODS Event: language_exec : Xact 3300000001 ORS#: 1, connId: 2
2013-12-17 23:27:08.06 spid51         Text:SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[FirstName] AS [FirstName], 
    [Extent1].[LastName] AS [LastName]
    FROM [dbo].[Customer] AS [Extent1]
2013-12-17 23:27:08.56 spid51      Parameter# 0: Name=,Flags=0,Xvt=231,MaxLen=154,Len=154,Pxvar Value=INSERT [dbo].[Customer]([Id], [FirstName], [LastName])
VALUES (@0, @1, @2)

2013-12-17 23:27:08.56 spid51      Parameter# 1: Name=,Flags=0,Xvt=231,MaxLen=102,Len=102,Pxvar Value=@0 uniqueidentifier,@1 varchar(256),@2 varchar(256)
2013-12-17 23:27:08.56 spid51      Parameter# 2: Name=@0,Flags=0,Xvt=36,MaxLen=16,Len=16,Pxvar Value=4318564C-304D-4792-85C5-F0113A873700
2013-12-17 23:27:08.56 spid51      Parameter# 3: Name=@1,Flags=0,Xvt=167,MaxLen=256,Len=5,Pxvar Value=ZanEF
2013-12-17 23:27:08.56 spid51      Parameter# 4: Name=@2,Flags=0,Xvt=167,MaxLen=256,Len=7,Pxvar Value=2c22266
2013-12-17 23:27:08.56 spid51          IPC Name: sp_executesql
2013-12-17 23:27:08.56 spid51      ODS Event: execrpc : Xact 3300000001 ORS#: 1, connId: 2
2013-12-17 23:27:08.58 spid51      ODS Event: Remote_ods : Xact 3300000001 ORS#: 1, connId: 1
2013-12-17 23:27:08.58 spid51      Xact COMMIT for Desc: 3300000001
2013-12-17 23:27:08.61 Server      ODS Event: Close conn 1 on spid 51
2013-12-17 23:27:08.61 Server      ODS Event: Close conn 2 on spid 51
2013-12-17 23:27:08.61 Server      ODS Event: Exit spid 51
2013-12-17 23:27:08.61 spid51      ODS Event: Logout connection 51
2013-12-17 23:27:08.61 Server      ODS Event: Close conn 0 on spid 51


We can pass NHUnitOfWork or EFUnitOfWork in to CustomerService and it will work as expected. Do avoid calling Unit Of Work within the Repository i.e. this.repositoryCustomer.Commit(), I've seen this a lot around the web. Unit Of Work should not be bound directly to it.

*Note: Code in this article is not production ready and is used for prototyping purposes only. If you have suggestions or feedback please do comment.


References: 

Saturday 7 December 2013

Publish Subscribe / Pipeline Pattern For MVP Example C#

Publish Subscribe is a great pattern, it's one of the patterns that really got me in to patterns in the first place.

The Problem
Not so long ago I was using model-view-presenter pattern with windows forms and web forms (feel free to check out my "Model View Presenter (MVP) for Win Forms and ASP.NET Examples" repository). Web form applications can potentially have many user controls on a page, if something happens in user control A then user control B or a web page needs to be updated. Windows forms data grids and controls might need to be refreshed if something happens on the child form.

You can use delegates/event handlers, and subscribe to changes. This is kind of a viable approach, but you are creating direct dependencies between different Presenters. Additionally, if you have many controls managing these delegates and handlers can become cumbersome.

Publish Subscribe Pattern C# - Before
Event handlers subscribing to different user controls, what a mess...


The Solution
Solution is very logical, inject a single pub/sub pipeline that will live for the lifetime of what's being rendered.

Publish Subscribe Pattern C# - After
Presenters subscribing to a single pub/sub pipeline. 

    public interface IPubSub : IDisposable
    {
        void Subscribe(IReceiver receiver, int messageId);

        void Publish<TEventArgs>(object sender, TEventArgs e, int messageId) 
            where TEventArgs : EventArgs;

        void Unsubscribe(IReceiver receiver, int messageId);
    }

 public sealed class PubSub : IPubSub
    {
        private readonly Dictionary<int, List<IReceiver>> messageIdToReceiver;

        public PubSub()
        {
            this.messageIdToReceiver = new Dictionary<int, List<IReceiver>>();
        }

        public void Subscribe(IReceiver receiver, int messageId)
        {
            List<IReceiver> receivers;

            if (this.messageIdToReceiver.TryGetValue(messageId, out receivers))
            {
                receivers.Add(receiver);
            }
            else
            {
                this.messageIdToReceiver.Add(messageId, new List<IReceiver>() { receiver });
            }
        }

        public void Publish<TEventArgs>(object sender, TEventArgs e, int messageId) 
            where TEventArgs : EventArgs
        {
            List<IReceiver> receivers;

            if (this.messageIdToReceiver.TryGetValue(messageId, out receivers))
            {
                foreach (IReceiver receiver in receivers)
                {
                    IReceiver<TEventArgs> receiverToReceive = receiver as IReceiver<TEventArgs>;

                    if (receiverToReceive != null)
                    {
                        receiverToReceive.Receive(sender, e, messageId);
                    }
                }
            }
        }

        public void Unsubscribe(IReceiver receiver, int messageId) 
        {
            List<IReceiver> receivers;

            if (this.messageIdToReceiver.TryGetValue(messageId, out receivers))
            {
                if (receivers.Count > 1)
                {
                    receivers.Remove(receiver);
                }
                else if(receivers.Count == 1)
                {
                    this.messageIdToReceiver.Remove(messageId);
                }
            }
        }

        public void Dispose()
        {
            this.messageIdToReceiver.Clear();
        }
    }


    public interface IReceiver<TEventArgs> : IReceiver
        where TEventArgs : EventArgs
    {
        void Receive(object sender, TEventArgs e, int messageId);
    }

    public interface IReceiver : IDisposable
    {

    }


The Example
  
 public class PresenterA : IReceiver<EventArgs>, IDisposable
    {
        readonly IPubSub pubSub;

        public PresenterA(IPubSub pubSub)
        {
            this.pubSub = pubSub;
            this.pubSub.Subscribe(this, (int)PubSubMsg.AppInstl);
        }

        public void Receive(object sender, EventArgs e, int messageId)
        {
            if ((PubSubMsg)messageId == PubSubMsg.AppInstl)
            {
                //Now that you received the message, update the UI, etc...
            }
        }

        public void Dispose()
        {
            this.pubSub.Unsubscribe(this, (int)PubSubMsg.AppInstl);
        }
    }

    public class PresenterB
    {
        readonly IPubSub pubSub;

        public PresenterB(IPubSub pubSub)
        {
            this.pubSub = pubSub;
        }

        public void btnApplicationRemove(object sender, EventArgs e)
        {
            //Do what you need to do and then publish the message
            this.pubSub.Publish<EventArgs>(this, e, (int)PubSubMsg.AppInstl);
        }
    }

    public enum PubSubMsg
    {
        AppInstl = 1
    }

PubSub class instance can be created by the IoC container. It should be instantiated for the life time of the page context. This means that it will be isolated from the rest of the application and messages will only be distributed around a single parent view.

When it comes to a Win Form it becomes a bit more complicated. Single PubSub class instance is created and shared through out the entire application. This is great, imagine that you are 4 to 7 screens in from the main home screen. If home screen has subscribed to these messages it will receive them and update it self. When users comes back the home screen there is no need to do anything as it's already synchronized.

The Conclusion
It's an elegant solution for cross Presenter communication. It removes dependencies, and enables memory based synchronous communications.

Friday 6 December 2013

MongoDB For Analytics C# Example


I was one of the Co-founders of Appacts Open Source Mobile Analytics. I've wrote the original version on top of SQL Server 2008 R2. It was something I felt very comfortable with. I've heard of MongoDB about 4 years prior to Appacts. As many developers who got used to RDBMS systems I didn't see much point in MongoDB, I thought I get it and there is no point to it. It's a Document storage thing, who cares, right?

The bias towards technology has impaired my judgment, I even made assumptions that I understood how it works. It's a shame that for so many years I ignored it, and stayed away from it. It's so powerful. Let's find out what made me get out my comfort zone.

The Problem
I am getting thousands of raw data requests coming through (per second). I need to record event data in a cost effective way, additionally I need to be able to look this data up within milliseconds to give user an amazing experience.

This is how a raw data object looks like:
    
  public class Event
    {
        public string Data { get; set; }
        public EventType EventTypeId { get; set; }
        public string EventName { get; set; }
        public Int64 Length { get; set; }
        public string ScreenName { get; set; }
        public DateTime DateCreated { get; set; }
        public DateTime DateCreatedOnDevice { get; set; }
        public DateTime Date { get; set; }
        public Guid SessionId { get; set; }
        public string Version { get; set; }
        public Guid ApplicationId { get; set; }
        public Guid DeviceId { get; set; }
        public PlatformType PlatformId { get; set; }
    }

Imagine storing this for every request and then trying to query this over months of data.


The Solution
MongoDB has the following functions that are incredibly powerful:

These little functions change everything and document databases are great for data portioning.
Take the raw data example above, what do we really need to get from it? We don't actually need to store it in a pure raw format and we don't need to aggregate it. Instead we can create a new portioned aggregate object.

 public class ContentLoadSummary : Summary
    {
        public List<ContentDurationAggregate> Loads { get; set; }

        public ContentLoadSummary()
        {

        }

        public ContentLoadSummary(Event eventItem)
            : base(eventItem)
        {
            this.Loads = new List<ContentDurationAggregate>();
            this.Loads.Add(new ContentDurationAggregate(eventItem.ScreenName, 
                eventItem.EventName, eventItem.Length / 1000));
        }

    }

    public class ContentDurationAggregate : DurationAggregate
    {
        public string Content { get; set; }
        public string ScreenContent { get; set; }

        public ContentDurationAggregate(string screen, string content, long seconds)
            : base(screen, seconds)
        {
            this.Content = content;
            this.ScreenContent = String.Concat(screen, content);
        }

        public ContentDurationAggregate CopyOnlyKey()
        {
            return new ContentDurationAggregate(this.Key, this.Content, 0);
        }
    }

    public class DurationAggregate : Aggregate<string>
    {
        public long Seconds { get; set; }

        public DurationAggregate(string key, long seconds)
            : base(key)
        {
            this.Seconds = seconds;
        }
    }

 public class Aggregate<TKey>
    {
        public TKey Key { get; set; }
        public int Count { get; set; }

        public Aggregate()
        {

        }

        public Aggregate(TKey key)
        {
            this.Key = key;
        }

        public Aggregate<TKey> CopyOnlyKeys()
        {
            return new Aggregate<TKey>(this.Key);
        }
    }


Summary is partition base, this allows us to create new or append existing document with different dates, application versions, application platforms, etc.
    
   public abstract class Summary
    {
        public ObjectId Id { get; set; }
        public string Version { get; set; }
        public DateTime Date { get; set; }
        public Guid ApplicationId { get; set; }
        public PlatformType PlatformId { get; set; }
        public int Count { get; set; }

        public Summary()
        {

        }

        public Summary(Item item)
        {
            this.Version = item.Version;
            this.Date = DateTime.SpecifyKind(item.Date, DateTimeKind.Utc);
            this.ApplicationId = item.ApplicationId;
            this.PlatformId = item.PlatformId;
            this.Count++;
        }

        public Summary(DeviceInfo deviceInfo, ApplicationInfo applicationInfo)
        {
            this.Date = DateTime.SpecifyKind(deviceInfo.Date, DateTimeKind.Utc);
            this.ApplicationId = applicationInfo.ApplicationId;
            this.PlatformId = deviceInfo.PlatformType;
            this.Version = applicationInfo.Version;
            this.Count++;
        }
    }

The Example
 

 //You create a query, you are trying to see if you can find the data in the collection that matches your parameters.
                IMongoQuery queryBase = Query.And
                    (
                        Query<ContentLoadSummary>.EQ<DateTime>(mem => mem.Date, entity.Date),
                        Query<ContentLoadSummary>.EQ<Guid>(mem => mem.ApplicationId, entity.ApplicationId),
                        Query<ContentLoadSummary>.EQ<string>(mem => mem.Version, entity.Version),
                        Query<ContentLoadSummary>.EQ<PlatformType>(mem => mem.PlatformId, entity.PlatformId)
                    );

                // If you query and data doesn't exist then you will need to create a new document with the parameters, if it does exist then you just want to .Inc(=>) (Increment) a count
                IMongoUpdate update = Update<ContentLoadSummary>
                    .SetOnInsert(x => x.Version, entity.Version)
                    .SetOnInsert(x => x.Date, entity.Date)
                    .SetOnInsert(x => x.ApplicationId, entity.ApplicationId)
                    .SetOnInsert(x => x.PlatformId, entity.PlatformId)
                    .SetOnInsert(x => x.Loads, new List<ContentDurationAggregate>())
                    .Inc(mem => mem.Count, entity.Count);

                this.GetCollection<ContentLoadSummary>().FindAndModify(queryBase, SortBy.Descending("Date"), update, false, true);


//You need to extend your original query to also do a look up on the collection.
IMongoQuery queryLoadInsert = Query.And
                    (
                        queryBase,
                        Query.NE("Loads.ScreenContent", BsonValue.Create(entity.Loads.First().ScreenContent))
                    );

                IMongoUpdate insertLoad = Update
                    .Push("Loads", BsonValue.Create(entity.Loads.First().CopyOnlyKey().ToBsonDocument()));

//Does a look up on the query and then pushes a record in to the collection if it doesn't find the record
                this.GetCollection<ContentLoadSummary>().Update(queryLoadInsert, insertLoad);

                //Does a look on the record, if it finds it then it Increments Seconds and Count
                IMongoQuery queryLoadUpdate = Query.And
                    (
                        queryBase,
                        Query.EQ("Loads.ScreenContent", BsonValue.Create(entity.Loads.First().ScreenContent))
                    );

                IMongoUpdate updateContentLoad = Update
                    .Inc("Loads.$.Seconds", entity.Loads.First().Seconds)
                    .Inc("Loads.$.Count", 1);

                this.GetCollection<ContentLoadSummary>().Update(queryLoadUpdate, updateContentLoad);

The Output
 
{ 
    "ApplicationId" : BinData(3,"JyVgNisUD0S/P4VvblnRpg=="), 
    "Count" : 3, 
    "Date" : ISODate("2013-05-25T00:00:00Z"), 
    "Loads" : 
           [ 
              {       "Key" : "Searching", 
                      "Count" : 10,    
                      "Seconds" : NumberLong(102),      
                      "Content" : "ContentLookup", 
                       "ScreenContent" : "SearchingContentLookup" 
              },
              {       "Key" : "Saving", 
                      "Count" : 3,    
                      "Seconds" : NumberLong(7),      
                      "Content" : "CalledNumber", 
                      "ScreenContent" : "SavingCalledNumber" 
              } 
           ], 
    "PlatformId" : 6, 
    "Version" : "1.6", 
    "_id" : ObjectId("51a26322fef1ae002dd60a73") 
}



The Conclusion
MongoDB is very powerful and it's here to stay. Polyglot persistence is the way forward and companies/individuals need to embrace it. It's all about using the right tool for the right job and trying not to be too biased. It's time to get out of your comfort zone.