Wednesday, March 27, 2013

Palermo/Miller stuff

using System;
using System.Web.Mvc;
using ShockCaperShivers.Core;
using StructureMap;
namespace ShockCaperShivers.Controllers
{
   public class HomeController : Controller
   {
      public ActionResult Index()
      {
         IClockRepository clockRepository = ObjectFactory.
               GetInstance<IClockRepository>();
         Clock clock = clockRepository.RetrieveClock();
         Hand hand = new Hand();
         hand.HandAtHand = "rock";
         hand.IpIdentity = "127.0.0.1";
         hand.TimestampIdentity = clock.CurrentTime;
         hand.UniqueIdentity = Guid.NewGuid();
         IHandRepository handRepository = ObjectFactory.GetInstance<IHandRepository>();
         handRepository.AddHand(hand);
         return View(hand);
      }
   }
}

 
 

What is happening in the code above? We are using ObjectFactory to come up with a Clock object, creating a Hand object, setting a bunch of getsetters on the Hand object including setting a DateTime value using the Clock object, and then handing the Hand object back to ObjectFactory. What is going to happen? I have a database table named Hand with columns for HandAtHand, IpIdentity, TimestampIdentity, and UniqueIdentity in it and a new row will be added to this database table. Why would I do this and why should you care? Alright, I have decided that I would like to know more about SignalR and to that end I have decided that I would like to build an AJAX-driven game of rock-paper-scissors in which one may play rock-paper-scissors online with the whole of humanity. If I can't get SignalR to work I'll just write the app using typical AJAX long polling. I envision each hand a player plays to correspond to a Hand object which will be saved to a Hand table at a database. I have set up, free of AJAX, the initial code for pushing a record into the database and I have done it faithful to the Onion Architecture one might associate with Jeffrey Palermo while using some of the open source tools one might associate with Jeremy Miller, namely StructureMap and also Fluent NHibernate. The image here shows the ASP.NET MVC 4 with Web API project (ShockCaperShivers) I am using to keep the Controller shown above and also two other projects (ShockCaperShivers.Core and ShockCaperShivers.Infrastructure) which I will explain in a moment. My original blog that I set up at Headspring has postings on this sort of thing here and here, but I've been out of Headspring for over two years now and the postings have fallen into disrepair in that their images became broken links. It's time to revamp! The rock-paper-scissors idea I have is the perfect excuse for a new blog posting and here it is! I hope you enjoy it.

using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using ShockCaperShivers.Core;
using ShockCaperShivers.Infrastructure;
using StructureMap;
namespace ShockCaperShivers
{
   public class WebApiApplication : System.Web.HttpApplication
   {
      protected void Application_Start()
      {
         ObjectFactory.Initialize(x =>
         {
            x.ForRequestedType<IClockRepository>
                  ().TheDefaultIsConcreteType<ClockRepository>();
            x.ForRequestedType<IHandRepository>
                  ().TheDefaultIsConcreteType<HandRepository>();
         });
         AreaRegistration.RegisterAllAreas();
         WebApiConfig.Register(GlobalConfiguration.Configuration);
         FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
         RouteConfig.RegisterRoutes(RouteTable.Routes);
         BundleConfig.RegisterBundles(BundleTable.Bundles);
      }
   }
}

 
 

Above is the Global.asax.cs for the ShockCaperShivers project. The only thing I added was the first line for ObjectFactory. This is needed to wire up StructureMap. StructureMap also needs a config file called StructureMap.config with its "Build Action" set to "Content" in the Properties pane and its "Copy to Output Directory" setting set to "Copy always" at the Properties pane. For me this file has the following XML in it:

<StructureMap>
   <PluginFamily Type="ShockCaperShivers.Core.IClockRepository"
         Assembly="ShockCaperShivers.Core" DefaultKey="Default">
      <Plugin Assembly="ShockCaperShivers.Infrastructure"
            Type="ShockCaperShivers.Infrastructure.ClockRepository"
            ConcreteKey="Default" />
   </PluginFamily>
   <PluginFamily Type="ShockCaperShivers.Core.IHandRepository"
         Assembly="ShockCaperShivers.Core" DefaultKey="Default">
      <Plugin Assembly="ShockCaperShivers.Infrastructure"
            Type="ShockCaperShivers.Infrastructure.HandRepository"
            ConcreteKey="Default" />
   </PluginFamily>
</StructureMap>

 
 

The associations above associate interfaces for repositories in the Core project with repository classes in the Infrastructure project. Both ShockCaperShivers.Core and ShockCaperShivers.Infrastructure are referenced by ShockCaperShivers so it, the user interface project, may act at bootstrapper and wire up these associations. The Infrastructure inherits from the Core only and the Core inherits nothing. The Core is going to hold the business logic free from external dependencies kept in the Infrastructure. In a three-layered approach the UI would inherit Core and Core would inherit Infrastructure, but in the Onion model the dependencies are injected into the Core against the flow of inheritance via StructureMap. This allows the logic in the core, free of external dependencies, to stay clean and be easy to test. By the way, if you think there should be a fourth project for tests, you're right. I'm just keeping it simple in this example. There are no more files that I wish to show off in the UI, but there are four files in the Core and four files in the Infrastructure that we should look at. Let's look at the Core first. Here is the Clock object class:

using System;
namespace ShockCaperShivers.Core
{
   public class Clock
   {
      public DateTime CurrentTime { get; set; }
      public Clock(DateTime time)
      {
         CurrentTime = time;
      }
   }
}

 
 

Here is the interface the Core will lean upon to get a new Clock:

namespace ShockCaperShivers.Core
{
   public interface IClockRepository
   {
      Clock RetrieveClock();
   }
}

 
 

Here is the Hand object:

using System;
namespace ShockCaperShivers.Core
{
   public class Hand
   {
      public virtual string HandAtHand { get; set; }
      public virtual Guid? HandOpposing { get; set; }
      public virtual string HandOutcome { get; set; }
      public virtual string IpIdentity { get; set; }
      public virtual DateTime TimestampIdentity { get; set; }
      public virtual Guid UniqueIdentity { get; set; }
   }
}

 
 

Here is the interface the Core will lean upon to push a Hand object to the Infrastructure project:

namespace ShockCaperShivers.Core
{
   public interface IHandRepository
   {
      void AddHand(Hand hand);
   }
}

 
 

The last four files were in the Core. The remaining four files are in the Infrastructure. The first one is the repository for the Clock interface. As you can now see all I am doing is wrapping DateTime.UtcNow in this repository and all the Clock object does is wrap DateTime.UtcNow. Why all this ceremony? The timekeeping is an external dependency. You do not want to sprinkle DateTime.UtcNow all over your code as it could be painful to replace with DateTime.Now or something else later on. This stuff (timekeeping) should really be driven from one locale in the Infrastructure.

using System;
using ShockCaperShivers.Core;
namespace ShockCaperShivers.Infrastructure
{
   public class ClockRepository : IClockRepository
   {
      public Clock RetrieveClock()
      {
         return new Clock(DateTime.UtcNow);
      }
   }
}

 
 

The next three files and also the last three files I'll show off, all have to do with the saving of Hand objects in the Infrastructure. The first one is a SQL script for creating the Hand table which serves no functional purpose in the application whatsoever. It is however a good idea to hang onto the SQL scripts that you use to craft SQL for your apps.

BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Hand
   (
   HandAtHand nvarchar(8) NOT NULL,
   HandOpposing uniqueidentifier NULL,
   HandOutcome varchar(255) NULL,
   IpIdentity nvarchar(28) NOT NULL,
   TimestampIdentity datetime NOT NULL,
   UniqueIdentity uniqueidentifier NOT NULL
   ) ON [PRIMARY]
GO
ALTER TABLE dbo.Hand ADD CONSTRAINT
   DF_Hand_IpIdentity DEFAULT (newid()) FOR IpIdentity
GO
ALTER TABLE dbo.Hand ADD CONSTRAINT
   PK_Hand PRIMARY KEY CLUSTERED
   (
   UniqueIdentity
   ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
ALTER TABLE dbo.Hand SET (LOCK_ESCALATION = TABLE)
GO
COMMIT

 
 

Our token table:

We need a map for each object we manipulate with Fluent NHibernate. Here is ours:

using ShockCaperShivers.Core;
using FluentNHibernate.Mapping;
namespace ShockCaperShivers.Infrastructure
{
   public class HandMap : ClassMap<Hand>
   {
      public HandMap()
      {
         Map(x => x.HandAtHand);
         Map(x => x.HandOpposing);
         Map(x => x.HandOutcome);
         Map(x => x.IpIdentity);
         Map(x => x.TimestampIdentity);
         Id(x => x.UniqueIdentity);
      }
   }
}

 
 

Finally here is our repository for Hand. I hope I have the using statements appropriate below to prevent the memory leak I experienced previously. "FluentNHibernateConnection" corresponds to an appSettings key in Web.config which holds a database connection string.

using ShockCaperShivers.Core;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
namespace ShockCaperShivers.Infrastructure
{
   public class HandRepository : IHandRepository
   {
      public void AddHand(Hand hand)
      {
         using (var sessionFactory = CreateSessionFactory())
         {
            using (var session = sessionFactory.OpenSession())
            {
               using (var transaction = session.BeginTransaction())
               {
                  session.Save(hand);
                  session.Flush();
                  transaction.Commit();
               }
            }
         }
      }
      
      private static ISessionFactory CreateSessionFactory()
      {
         return Fluently.Configure().Database(MsSqlConfiguration.MsSql2005
               .ConnectionString(c => c.FromAppSetting("FluentNHibernateConnection")))
               .Mappings(m => m.FluentMappings.AddFromAssemblyOf<HandRepository>
               ()).BuildSessionFactory();
      }
   }
}

No comments:

Post a Comment