20070320

Unit of Work Pattern - Take 2

I attempted the Unit of Work pattern a couple of times (here) and (here)

But I think during the JP bootcamp, we were talking about how the implementation in the Fowler book is very cluttered. Domain objects know about the unit of work, bla, bla bla... I decided to take a stab on implementing a UoW with Domain objects not knowing about the pattern... Before I go, I kind of cheated with making my domain object know how to mark it self.

And if you want the source code, download it (here).

The Test




    1 using System.Collections.Generic;

    2 using MbUnit.Framework;

    3 using Rhino.Mocks;

    4 using unitofwork.spike.Dataaccess;

    5 using unitofwork.spike.Domain;

    6 using unitofwork.spike.utility;

    7 

    8 namespace unitofwork.spike.test.Task

    9 {

   10     [TestFixture]

   11     public class UnitOfWorkTest

   12     {

   13         private MockRepository mockery;

   14         private IDatabaseConnection connection;

   15         private IMapper mapper;

   16 

   17         [SetUp]

   18         public void Setup()

   19         {

   20             mockery = new MockRepository();

   21             mapper = mockery.CreateMock<IMapper>();

   22             connection = mockery.CreateMock<IDatabaseConnection>();

   23             DependencyResolver.Add(typeof (FakeUser), mapper);

   24         }

   25 

   26         [TearDown]

   27         public void TearDown()

   28         {

   29             mockery.VerifyAll();

   30             DependencyResolver.Remove(typeof (FakeUser));

   31         }

   32 

   33         [Test]

   34         public void ShouldInstanciate()

   35         {

   36             mockery.ReplayAll();

   37             IUnitOfWork work = new UnitOfWork(connection);

   38 

   39             Assert.IsNotNull(work);

   40         }

   41 

   42         [Test]

   43         public void Commit_ShouldCommit_OnlyRegisterNew()

   44         {

   45             FakeUser user = new FakeUser();

   46 

   47             mapper.Save(connection, user);

   48             connection.Dispose();

   49 

   50             mockery.ReplayAll();

   51 

   52             IUnitOfWork work = new UnitOfWork(connection);

   53 

   54             work.RegisterNew(user);

   55             work.Commit();

   56         }

   57 

   58         [Test]

   59         public void Commit_ShouldCommitABunchOfStuff()

   60         {

   61             IList<IDomainObject> fakeUsers = new List<IDomainObject>();

   62             FakeUser firstUser = new FakeUser();

   63             FakeUser secondUser = new FakeUser();

   64             FakeUser thirdUser = new FakeUser();

   65             FakeUser fourthUser = new FakeUser();

   66             FakeUser fifthUser = new FakeUser();

   67 

   68             firstUser.Mark = Mark.New;

   69             secondUser.Mark = Mark.Dirty;

   70             thirdUser.Mark = Mark.Delete;

   71 

   72             fakeUsers.Add(firstUser);

   73             fakeUsers.Add(secondUser);

   74             fakeUsers.Add(thirdUser);

   75             fakeUsers.Add(fourthUser);

   76             fakeUsers.Add(fifthUser);

   77 

   78             mapper.Save(connection, firstUser);

   79             mapper.Update(connection, secondUser);

   80             mapper.Delete(connection, thirdUser);

   81 

   82             connection.Dispose();

   83 

   84             mockery.ReplayAll();

   85 

   86             IUnitOfWork work = new UnitOfWork(connection);

   87 

   88             work.Register(fakeUsers);

   89             work.Commit();

   90         }

   91 

   92         private class FakeUser : IDomainObject

   93         {

   94             private Mark mark;

   95 

   96             public Mark Mark

   97             {

   98                 get { return mark; }

   99                 set { mark = value; }

  100             }

  101         }

  102     }

  103 }




The Code




    1 using System.Collections.Generic;

    2 using unitofwork.spike.Dataaccess;

    3 using unitofwork.spike.Domain;

    4 

    5 namespace unitofwork.spike.utility

    6 {

    7     public class UnitOfWork : IUnitOfWork

    8     {

    9         private readonly IDatabaseConnection connection;

   10         private IList<IDomainObject> registerNew;

   11         private IList<IDomainObject> registerDirty;

   12         private IList<IDomainObject> registerDelete;

   13 

   14         public UnitOfWork(IDatabaseConnection connection)

   15         {

   16             this.connection = connection;

   17             registerNew = new List<IDomainObject>();

   18             registerDirty = new List<IDomainObject>();

   19             registerDelete = new List<IDomainObject>();

   20         }

   21 

   22         public UnitOfWork() : this (DependencyResolver.GetImplementationOf<IDatabaseConnection>(typeof(IDatabaseConnection)))

   23         {

   24         }

   25 

   26         public void RegisterNew(IDomainObject domainObject)

   27         {

   28             registerNew.Add(domainObject);

   29         }

   30 

   31         public void RegisterDirty(IDomainObject domainObject)

   32         {

   33             registerDirty.Add(domainObject);

   34         }

   35 

   36         public void RegisterDelete(IDomainObject domainObject)

   37         {

   38             registerDelete.Add(domainObject);

   39         }

   40 

   41         public void Register(IList<IDomainObject> domainObjects)

   42         {

   43             foreach (IDomainObject domainObject in domainObjects)

   44             {

   45                 if (domainObject.Mark != null)

   46                 {

   47                     if (domainObject.Mark == Mark.New)

   48                         RegisterNew(domainObject);

   49 

   50                     if (domainObject.Mark == Mark.Dirty)

   51                         RegisterDirty(domainObject);

   52 

   53                     if (domainObject.Mark == Mark.Delete)

   54                         RegisterDelete(domainObject);

   55                 }

   56             }

   57         }

   58 

   59         public void Commit()

   60         {

   61             using (connection)

   62             {

   63                 try

   64                 {

   65                     Save(connection);

   66                     Update(connection);

   67                     Delete(connection);

   68                 }

   69                 catch

   70                 {

   71                     connection.Rollback();

   72                 }

   73             }

   74         }

   75 

   76         private void Delete(IDatabaseConnection connection)

   77         {

   78             foreach (IDomainObject domainObject in registerDelete)

   79             {

   80                 DependencyResolver.GetImplementationOf<IMapper>(domainObject.GetType()).Delete(connection, domainObject);

   81             }

   82         }

   83 

   84         private void Update(IDatabaseConnection connection)

   85         {

   86             foreach (IDomainObject domainObject in registerDirty)

   87             {

   88                 DependencyResolver.GetImplementationOf<IMapper>(domainObject.GetType()).Update(connection, domainObject);

   89             }

   90         }

   91 

   92         private void Save(IDatabaseConnection connection)

   93         {

   94             foreach (IDomainObject domainObject in registerNew)

   95             {

   96                 DependencyResolver.GetImplementationOf<IMapper>(domainObject.GetType()).Save(connection, domainObject);

   97             }

   98         }

   99     }

  100 }



The other Crap




    1 namespace unitofwork.spike.Domain

    2 {

    3     public interface IDomainObject

    4     {

    5         Mark Mark { get; set; }

    6     }

    7 }




    1 namespace unitofwork.spike.Domain

    2 {

    3     public sealed class Mark

    4     {

    5         public static readonly Mark New = new Mark();

    6         public static readonly Mark Dirty = new Mark();

    7         public static readonly Mark Delete = new Mark();

    8     }

    9 }




    1 using unitofwork.spike.Domain;

    2 

    3 namespace unitofwork.spike.Dataaccess

    4 {

    5     public interface IMapper

    6     {

    7         void Save(IDomainObject domainObject);

    8         void Save(IDatabaseConnection connection, IDomainObject domainObject);

    9         void Update(IDomainObject domainObject);

   10         void Update(IDatabaseConnection connection, IDomainObject domainObject);

   11         void Delete(IDomainObject domainObject);

   12         void Delete(IDatabaseConnection connection, IDomainObject domainObject);

   13     }

   14 }

20070311

Castle Projects - Active Record

I don't know about you, but I'm really lazy with bustin' some SQL madness, data mapper tests are a pain, nhibernate .hbm.xml files are a pain!!!

What can a simple developer do to quickly build up a data access layer, and still look pimp?

I came across ActiveRecord by CastleProjects a couple of months ago. It is using ofcourse the active record pattern shown on that Fowler book about enterprise patterns. It uses nhibernate libraries without the pain of creating those damn hibernate files. It just uses System.Attribute

Below is a sample code that I did up just to see how fast it was for me to create a dataaccess layer:


ofcourse we have to start with a Test...



using cargo.domain;

using MbUnit.Framework;

 

namespace cargo.test

{

    [TestFixture]

    public class UserTest_DataAccess

    {

        [Test]

        public void ShouldHandleActiveRecordFunctionality()

        {

            Company company = DomainObjectFactory.CreateTestCompany();

            company.Save();

 

            User user = new User();

            user.UserName = "jonas";

            user.Password = "test";

            user.Company = company;

 

            user.Save();

 

            AssertUser(User.FindBy(user.UserName), user);

 

            user.UserName = "updated";

            user.Password = "updated2";

 

            user.Update();

 

            AssertUser(User.FindBy(user.Id), user);

 

            user.Delete();

 

            Assert.IsNotNull(Company.FindBy(company.Id));

            company.Delete();

            Assert.IsNull(Company.FindBy(company.Id));

 

 

            Assert.AreEqual(0, User.FindAll().Length);

        }

 

        private static void AssertUser(User result, User user)

        {

            Assert.AreEqual(result.UserName, user.UserName);

            Assert.AreEqual(result.Password, user.Password);

        }

    }

}






and now the code....



using Castle.ActiveRecord;

using NHibernate.Expression;

 

namespace cargo.domain

{

    [ActiveRecord("CARGO_USER")]

    public class User : ActiveRecordBase<User>, IUser

    {

        private int id;

        private string userName;

        private string password;

        private int status;

        private Company company;

 

        public User()

        {

            ActiveRecordInitializer.Initialize(GetType().Assembly);

        }

 

        public static User FindBy(string userName, string password)

        {

            return FindOne(Expression.Eq("UserName", userName), Expression.Eq("Password", password));

        }

 

        public static User FindBy(string userName)

        {

            return FindOne(Expression.Eq("UserName", userName));

        }

 

        public static User FindBy(int id)

        {

            return FindOne(Expression.Eq("Id", id));

        }

 

        [PrimaryKey(PrimaryKeyType.Native, "user_id")]

        public int Id

        {

            get { return id; }

            set { id = value; }

        }

 

        [Property("user_name")]

        public string UserName

        {

            get { return userName; }

            set { userName = value; }

        }

 

        [Property("user_password")]

        public string Password

        {

            get { return password; }

            set { password = value; }

        }

 

        [Property("user_status")]

        public int Status

        {

            get { return status; }

            set { status = value; }

        }

 

        [BelongsTo("COMPANY_ID")]

        public Company Company

        {

            get { return company; }

            set { company = value; }

        }

    }

}


20070310

.Net Developer Kickin it with my MacBook Pro



I've been using a Mac for almost a year now. Been using the 1st relase of the 15" MacBook Pro. What do I have to say about the experience? I love it!

I decided to get out of the windows world because of one thing. I wanted to have an OS that does all the stuff that I need to do for personal stuff such as picture maintenance, movie making, music production, etc... since the mac has iLife and GarageBand, I was sold.

Specs:
MacBook Pro Core Duo 2.16 Ghz
2 Gigs of RAM
100GB HDD

I was one of the people that purchased Parallels Desktop for $30 when it was still in Beta. Popped my XP CD, and been developing crazy apps on it.

XP on Parallels Specs:
XP Pro SP2
1.1GB of Ram
Coherence Mode
SQL Server 2005 Standard
Oracle 10g Standard
Visual Studio 2005 with Resharper

You may be asking "How do you like developing windows apps in Parallels?" It's kick ass! Definately separates my "work" with my "life" when it comes to systems. And parallels handles XP like nobody's business.







But then Windows Vista came along. I had Vista running in parallels for a little bit, but that damn OS is a resource HOG!!!

I have been avoiding BootCamp because I don't like rebooting to another OS just to do a specific task. But Vista is a pain and plus trying to be in the bleeding edge in .net development with all the 3.0 framework stuff that is coming, I had to install vista on bootcamp.



Vista on bootcamp was a painless install. I dont want to discuss how I installed it. Just google "Bootcamp Vista" and there are a lot of tutorials how to pimpafy it up.

Vista on Bootcamp Specs:
Vista Ultimate
SQL Server 2005 Std
VS.Net 2005 with Resharper


So if you are a .Net developer and wants a change, would I recomend this Mac stuff? HELL YES!!.

**UPDATE: I do feel dirty running windows on my mac. But gotta pay the bills.