Click here to Skip to main content
15,868,006 members
Articles / Web Development / ASP.NET

Inversion of Control/Dependency Injection with Repository Pattern, Fluent Nhibernate, and LightCore Framework

Rate me:
Please Sign up or sign in to vote.
4.89/5 (25 votes)
17 Dec 2012MIT17 min read 110.3K   3.8K   126   17
Using IOC/DI and Repository Factory with LightCore.

Introduction

The more we reuse existing code in our systems and applications the better off we are at implementing modifications, changes, and enhancements that may be required later. The real challenge is coalescing together heterogeneous components effectively without a high level of dependency. This article is not intended to indoctrinate you on cohesion versus coupling, patterns (though we will incorporate a few), or even the low-level mechanics of Inversion of Control/Dependency Injection, as there exists many articles on the internet that describe these paradigms in great detail. The focus of this article is to provide one of many approaches of implementing Inversion of Control using a repository pattern, Fluent NHibernate ORM, and the LightCore IoC/DI framework to solve dependency issues in an intuitive way. In addition, this article is focused primarily for software engineers who are having trouble implementing the concept of IOC/DI. I will assume you've never written one line of code to solve dependency problems using IoC/DI nor used any related frameworks. After accomplishing an exhaustive research of IOC and DI on the internet I found very few examples that explain in moderate detail of how to use this technique using LightCore.

Main Points

Part 1 - Concepts

Part 2 - Hands On

Background

This article assumes that you are at least proficient in .NET 3.5/4.0 Frameworks, ASP.NET, C#, Object Oriented programming and some patterns. In addition, you'll need to have some experience writing N-Tier architectures using an ORM, SQL Server 2005/2008, and Unit Testing.

Here are some links you may find useful in negotiating this article:

The Problem

Dependency. At some point in your career as a software engineer, developer, tester, or architect, you'll likely encounter a problem that is either directly related to dependency or is a byproduct of high dependent states and behaviors that exist in your code. Dependency causes challenges in software systems because it introduces reliance or coupling which makes it difficult, and in some cases impossible, to change a related module without being intrusive on other parts of your application. Let's look at the following example.

IOC_Works/cDia.png

The dependency diagram above (courtesy of of Visual Studio 2010 Visualization and Modeling) shows that ClassA relies on ClassB because if ClassA needs to invoke or execute the 'DoSomething' method on ClassB, it needs to create an instance of ClassB. ClassA has control of creating the instance (object) and "knows" internal details about ClassB. If we were to change ClassB's method or any relevant structure of ClassB, we run the risk of affecting all classes that depend on ClassB - not good! Other challenges that tight coupling presents are increased efforts of reverse engineering through taxonomy, difficulty in testing, and decomposition back to high level abstraction.

C#
// Here is the source code for the illustration above
// showing the dependency
 
public class ClassA 
{ 
    private ClassB classBeesObject; 
 
    public ClassA() 
    { 
        classBeesObject = new ClassB(); 
    } 
} 
 
public class ClassB 
{ 
    protected void DoSomething() { }
}

The code above clearly shows ClassA taking control of creating ClassB's object (classBeesObject) and shows even further dependency by referencing ClassB locally. This tight relationship now introduces a heterogeneous architecture in which any change in ClassB could affect ClassA. Since transparency of implementation of ClassB now resides in ClassA, we now have the ingredients for logic mixing, and if the system grows in size and complexity, confusion.

The Solution: Delegating Responsibility

Inversion of Control (IOC): This technique needs little introduction as a search on the internet will bring a plethora of results explaining Inversion of Control. I'll jump on the bandwagon and add my two cents by stating that Inversion of Control or IOC is said to be the inverse of flow control or creational control through an abstract design. Well, what does this really mean? This definition and many other refer to the flow of a program normally attributed to procedural programming where specific code blocks incorporate another portion of code therefore having knowledge about the incorporated code's implementation (concrete methods, etc.) and controlling the flow. In the above code ClassA creates an instance of ClassB so ClassA is in control and knows about ClassB's implementation which in this case is the 'DoSoemthing' method. What we want to do is relinquish ClassA's control and give it to a third party. We also want ClassA to be oblivious to ClassB's implementation so in other words ClassA has no transparency in ClassB or even cares about what's 'inside' ClassB. Inversion of Control aims to accomplish this though the "Inversion" part is more of a misnomer as I tend to think that the control is 'delegated' to an abstraction. Using an IOC framework transfers control from, in this scenario, ClassA to another object designated by the IOC. Why does this help solve our dependency problem? Well, ClassB can be modified with no or very little intrusion on ClassA. We can now test ClassB without affecting ClassA. For instance, If ClassA uses ClassB to connect to an I/O Serial Port Server and lookup packet info we can easily run unit tests on ClassA excluding any operations that ClassB is accomplishing. So we reduce the chance of ClassA failing because ClassB fails to connect to the serial port or authenticate against a packet. Functional decomposition becomes less cumbersome because we can now illustrate functional relationships that are independent of reliance of other objects.

Dependency Injection (DI): You rarely hear of Inversion of Control without Dependency Injection being included in the conversation. In some instances, I've seen them explained as being the same thing but nothing could be further from the truth. IOC and DI is not the same thing but is closely related. Remember ClassB's reference variable (or dependency) we created in ClassA? Well what if we had a third party that gives us (or injects) this dependency without giving us what, when, how, or why? Dependency Injection accomplishes exactly that. DI "injects", or more specifically, hands you your reference variables (dependencies). I will not go into depth about Dependency Injection because you can research DI on the internet or purchase many publications that describe DI in great detail. There are many ways that dependency can be "handed" to you but for the sake of this article we will use what is commonly referred to as Containers.

Containers: We can solve a good portion of our dependency problems by using a container or more specifically the LightCore Framework (Container). The container is code that leverages abstraction which manages objects, instantiations, and configurations. The container in its simplest form determines the type of object to instantiate or inject and where to inject it. It's called a container because in essence it "holds" objects that are to be injected later with, of course, some lifecycle management properties. You can research more on DI containers via the internet. LightCore will provide the Container mechanism for us in this scenario.

Choosing Your IOC/DI Framework

Choosing your IOC/DI Framework depends on many factors (assuming you don't want to write your own custom IOC and DI). My reason hinges on ease of use and implementation, specific problems I intend it to solve, and the footprint it would impose on my software system in size, performance, and overhead. The size of the project may also be a deciding factor. LightCore is a lightweight framework and is great regarding all these attributes I mentioned, but falls somewhat short on documentation. In addition, LightCore is a German based framework so most of the site's information needs to be translated which normally occurs through Google's boiler plate translation mechanisms that render some of the logic difficult to understand. There's a variety of frameworks to choose from such as StructureMap, CastleWindsor, Spring .NET, Ninject, AutoFac, and Unity, just to name a few. For the purpose of this article I will use the LightCore framework but once you get the concept down any framework will become easy to implement.

Create an MVC 3 Project

OK, enough with the concepts, let's get started! You will need Microsoft's MVC 3 installed for this article. If you don't have it, you can get it from http://www.asp.net.mvc/mvc3. After installing MVC 3, open Visual Studio 2010 and select File > New > Project > Visual C#, and then select ASP.NET MVC 3 Web Application from the list, as shown below. Name the project Kodiak.

IOC_Works/MvcSetup.png

Select Internet Application and ASPX as the engine. Leave Unit Test unselected as we will add custom unit tests later.

IOC_Works/mvcsetupengine.png

Your Solution Explorer should look similar to this:

IOC_Works/MvcSetup3.png

Setting up the NTier

You can setup your Ntier any way you want so long as it illustrates the logical architecture that best suits your requirements. For the purpose of this article, I separate the layers themselves into different projects; however, if this was a commercial app, I would certainly create a folder for each project.

First using Visual Studio 10 add the DataLayer project by selecting File > AddProject > Visual C#, and select Class Library. Rename the default given name to KDatalayer. Since we are setting up the infrastructure of our application, let's go ahead and create the additional needed projects.

Just as we did for the KDatalayer, create additional Class Library projects and give them the following names: KLModel, KCore, and KTests (I excluded the BLL for simplicity). Your Solution Explorer should now look similar to this:

IOC_Works/NTier.png

Now right click on the KDataLayer project and add the following folders: Contracts, Entities, Maps, Repositories, and Sessions. The KDatalayer should now look like this:

IOC_Works/DLayer.png

Now let's add some references to the IOC framework, Fluent NHibernate and the LightCore framework. Using the links provided above download these components and add references to them in the KDatalayer project. Your Solution Explorer should look like this (Elmah and log4Net are optional):

IOC_Works/DLLs.png

Configure System Settings Properties

Let's create a contract (Interface) and a concrete Class that handles system settings. This contract allows us access to the properties settings for dynamic storing and retrieval of system settings properties, or more simply, information contained in the web.config or (if applicable) app.config files. Right click the KCore project in Solution Explorer and add two new folders and name one of the newly created folders 'Contract' and name the other folder 'Settings' (without the single quotes). Add a new interface in the Contracts folder and insert the following code:

C#
// Here is the source code for the Systems Settings contract 
 
using System;
using System.Collections.Generic;
using System.IO;
 
namespace KCore.Contracts
{
    interface ISystemSettings
    {
        /// <summary>
        /// Connection string 
        /// </summary>
        string KodiakConnectionString { get; }
    }
}

Add a new class in the Settings folder of the KCore project and insert the following code:

C#
// Here is the source code for the Systems Settings implementation
using KCore.Contracts;

namespace KCore.Settings
{
    class SystemSettings : ISystemSettings
    {
        public string KodiakConnectionString
        {
            get { return Properties.Settings.Default.KodiakDatabase; }
        }
    }
}

The KCore project in Solution Explorer should look similar to this:

IOC_Works/KCoreSttg.png

Now to create the settings property file, right click on KCore project and select Properties. Select the Settings tab on the left and in the name section, type KodiakDatabase, in the Type section, select (ConnectionString) from the dropdown list and leave Scope set at the default which is Application. Here's a quick view of how it should look:

IOC_Works/SysStg.png

Create Service Locator and Configure the Container

The Service Locator we will use is not much more than a pull-based, lookup pattern that locates services by using prescribed methods provided by the LightCore framework. More simply stated, the services are dependencies that can be preregistered and "pulled" upon request. The Service Locator can instantiate objects and provide other configuration schemes based on parameters passed to it. More importantly the Service Locator allows us to "wrap" LightCore's IOC framework's prescribed methods so that if we wanted to implement a different framework, such as StructureMap or Ninject, we could replace LightCore with ease. Create a new class and name it ServiceLocator and add to the root of the KCore project. Replace the ServiceLocator class code with the code provided below.

The ContainerBuilder object (builder) is used to simply store types of dependencies that have been registered. The container is an immutable that resolves types that have been registered. Parameterless constructors are excluded from registration for reasons I will not discuss here. If you're wondering what registration means exactly don't worry we'll cover it later in this article.

C#
// Service Locator Class

using LightCore;
using LightCore.Configuration;
using LightCore.Registration;
namespace KCore
{
    public static class ServiceLocator
    {
        // Create the container
        private static IContainer _container;
        /// <summary>
        /// Resolve a type for the container and return the type
        /// </summary>
        /// <typeparam name="T">Type to resolve</typeparam>
        public static T Resolve<T>()
        {
            if (_container == null) Configure("LightCore.config");
            return (T)_container.Resolve(typeof(T));
        }
        /// <summary>
        /// Prepare container configuration
        /// </summary>
        public static void Configure(string filename)
        {
            // Check if the container is null; 
            if (_container != null)
            {
                // Do something; like log and return
                //return;
            }
            // Here is hwere we intialize and build the LightCore IOC container
            // using the LightCore configuration file
            var builder = new ContainerBuilder();
            RegistrationModule xamlModule = new XamlRegistrationModule(filename);
            builder.RegisterModule(xamlModule);
            _container = builder.Build();
        }
    }
}

OK it's time to build the session factory and describe the need for it. Since we are using Fluent NHibernate Object Relational Mapping (ORM), we need to manage when the data is "staged" for our retrieval from the database. For this article, we are using MS SQL Server 2008 but SQL Server 2005 will work too. The Session Factory is Hibernate's concept of an atomic repository. The session factory is considered threadsafe which means multiple concurrent sessions can be requested and executed simultaneously. Normally only one Session Factory is required for the entire application and should be encapsulated inside a singleton. The LightCore DI framework will provide the Singleton implementation so we don't have to worry about writing our own though we could if necessary. Understanding what purpose the Session Factory serves primarily depends on our knowledge of what a "session" provides. A session itself issues queries to the database that returns the data into objects which are then "staged" or held for subsequent use. The "factory" concept hardly differs from any other factory pattern scheme; creating objects from objects. So the short description is the Session Factory spits out data filled objects upon request.

Let's create the SessionFactory contract.

In the Contracts folder in the KDatalayer project, add an Interface and name it ISessionFactory. This contract just uses the "boiler plate" session method from Fluent NHibernate to manage sessions. Place the code below in your ISessionFactory interface you just created.

C#
// Here is the source code for the Session Factory contract

using NHibernate;
namespace KDatalayer.Contracts
{
    interface ISessionFactory
    {
        ISession OpenSession();
    }
}

Let's create the concrete implementation of the ISessionFactory contract. Create a new class in the Sessions folder of the KDatalayer project and replace the code with the code provided below.

C#
// Here is the source code for Sessions

using System.Reflection;
using System.Runtime.CompilerServices;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Cfg;
using System.IO;
using KDataLayer.Contracts;
using KSTCore.Settings;

namespace KDatalayer.Sessions
{
    /// <summary>
    /// Factory to create nhibernate session.
    /// </summary>
    public class SessionFactory : ISessionFactory
    {
        private readonly ISystemSettings _settings;
        private readonly ISessionFactory _sessionFactory;
        
        // Here is the Singleton session 
        private ISession _session; 
        
        public SessionFactory(ISystemSettings settings)
        {
            _settings = settings;
            _sessionFactory = Fluently.Configure()
                .Database(
                    MsSqlConfiguration.MsSql2008
                 .ConnectionString(settings.KodiakConnectionString))
                .Mappings(m => m.FluentMappings.AddFromAssembly(Assembly
                .GetExecutingAssembly()))
                .BuildSessionFactory();
        }
    }
}

Create LightCore Configuration File and Register Dependencies

Now that we've finished the contracts and implementation of our dependencies, System Settings, and the Session Factory, we need to register these dependencies. The LightCore framework has a few ways of registering dependencies but I will only cover one of them. We will register our dependencies by creating a simple new web.config file in VS2010 and writing the LightCore handlers into it. Right click on the Kodiak project then select Add > New Item > Web > Configuration File. Rename the file from the default name to LightCore.config. Replace the code with the code below. Important: Ensure that "Build Action" is set to Content and "Copy to Output Directory" is set to Copy Always in the Properties section of the LightCore.config file. Check this by right clicking on the LightCore.config file and selecting Properties.

C#
<!--Here is the source code for your newly created web.config or app.config-->

<?xml version="1.0" encoding="utf-8" ?>
<LightCoreConfiguration 
   xmlns="clr-namespace:LightCore.Configuration;assembly=LightCore.Configuration">
  <LightCoreConfiguration.Registrations>
    <!-- Registrations -->
    <Registration ContractType="KCore.Contracts.ISystemSettings, KCore"
                  ImplementationType="KCore.Settings.SystemSettings, KCore"/>
    <Registration ContractType="KDatalayer.Contracts.IKSessionFactory, KDatalayer"
                  ImplementationType="KDatalayer.Sessions.SessionFactory, KDatalayer"/>
  </LightCoreConfiguration.Registrations>
</LightCoreConfiguration>

Remember when we built our container it was stated that it stores registered dependencies and resolves the types? Well, the dependencies stored are registered (in this scenario) using the LightCore.config file above. Using the LightCore.config file is one of several ways to register our dependencies. As you can see we register the fully qualified names of both the contracts and implementation of the SystemSettings and SessionFactory Dependencies. The format for both the ContractType and ImplementationType as per LightCore's instructions is Namespace.Type, Assembly and as you can see, we follow this format above with:

For example, according to the code above, our contract type for ISystemSettings is ...

Namespace.Type is: KCore.Contracts.ISystemSettings

Assembly is: KCore

The image below illustrates our current solution structure representing the LightCore registration:

IOC_Works/Reg.png

You will need to set the LightCore.config file path here:

C#
<!--Place this line of code in the MvcApplication Class in the Global.asax.cs file -->
private const string ConfigurationFilePath = @"~\LightCore.config";

Since we are using Fluent NHibernate ORM we will not use a "traditional" XML mapping scheme. Instead we will map to a simple database table using strongly typed C# code. If you don't have Fluent NHibernate please refer to Fluent NHibernate's website via the link provided several sections above and download the latest release. Refer to the installation section on their site for a more detailed description of how to use this ORM. We will use the LightCore framework along with repositories to write, edit, and delete data in a single table. The table name is AssignedPersons and you can generate this table by executing the script below in MS SQL Server Management Studio 2005/2008.

For this scenario, we're going to map a simple domain to the Assigned Persons table.

C#
// Here is the SQL code for the AssignedPerson table 

USE [Your Database Here]
GO
/****** Object:  Table [dbo].[AssignedPerson]*******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[AssignedPerson](
 [AssignedPersonId] [int] IDENTITY(1,1) NOT NULL,
 [FirstName] [nvarchar](255) NULL,
 [MiddleName] [nvarchar](255) NULL,
 [LastName] [nvarchar](255) NULL,
 [AssignmentStart] [datetime] NULL,
 [AssignmentEnd] [datetime] NULL,
 [Assignment_id] [int] NULL,
 [Installation_id] [int] NULL,
PRIMARY KEY CLUSTERED 
(
 [AssignedPersonId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, 
       IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, 
       ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

Here's the mapping to the table above using Fluent NHibernate API. To explain ORM or Object Relational Mapping further I will say that in short it's the act of traversing data between a set of objects and an associated underlying data source or repository(s). The main advantage of using an ORM is the ability to encapsulate changing data from your presentation layer and other unrelated subsystems. Of course there are numerous other advantageous for using an ORM.

A more practical reason why we use an ORM in this scenario is because it further strengthens our case regarding dependency. As I will show later by encapsulating the changes that originated from the underlying data source only the ORM is affected and the rest of the application (primarily) is insulated from any effects of the change. We can easily execute Unit Tests against the ORM without being intrusive on any other part of the application or relying on other modules that otherwise would be needed to return successful tests. You can research more on Fluent NHibernate's ORM from the link provided a few section up in this article.

C#
// Here is the source code for AssignedPerson Table Mapping

using FluentNHibernate.Mapping;
using KDatalayer.Entities;

namespace KDatalayer.Maps
{
   public class AssignedPersonMap : ClassMap<AssignedPerson>
    {
       public AssignedPersonMap()
       {
           Id(pi => pi.Id).Column("AssignedPersonId");
           Map(pi => pi.FirstName);
           Map(pi => pi.MiddleName);
           Map(pi => pi.LastName);
           Map(pi => pi.AssignmentStart);
           Map(pi => pi.AssignmentEnd);
           Map(pi => pi.Assignment_id);
           Map(pi => pi.Installation_id);
       }
    }
}

Below are the entities for the AssignedPerson class. These are a collection of objects that are used to persist the data to the database. An entity, such as the string property 'FirstName' below, is mutable which means its state can change. By creating an instance of the AssignedPerson class, for example, AssignedPerson aPerson = new AssignedPerson(), the state of aPerson is transient. Other entity states I will not get into are persisted and detached. For now, just understand that entities are objects that can be manipulated and saved to the database.

C#
// Here is the source code for the Fluent NHibernate Assigned Person entities

using System;

namespace KDatalayer.Entities
{
    /// <summary>
    /// AssignedPerson Entities
    /// </summary>
   public class AssignedPerson
    {
        public virtual Int32 Id { get; set; }
        public virtual String FirstName { get; set; }
        public virtual String MiddleName { get; set; }
        public virtual String LastName { get; set; }
        public virtual DateTime? AssignmentStart { get; set; }
        public virtual DateTime? AssignmentEnd { get; set; }
        public virtual Int32 Assignment_id { get; set; }
        public virtual Int32 Installation_id { get; set; }
    }
}

Here I use a model class to simply bind and map posted form values to my entities class. This may seem to bloat the architecture a little, but I feel that doing it this way helps explain a simple approach of representing the domain and how it could possibly be used for model first development if desired later.

C#
// Here is the source code for the Assigned Person model

using System;
namespace KModel
{
    /// <summary>
    /// AssignedPerson Model
    /// </summary>
    public class AssignedPersonModel
    {
        public virtual int Id { get; set; }
        public virtual String FirstName { get; set; }
        public virtual String MiddleName { get; set; }
        public virtual String LastName { get; set; }
        public virtual DateTime? AssignmentStart { get; set; }
        public virtual DateTime? AssignmentEnd { get; set; }
        public int AssignmentId { get; set; }
        public int InstallationId { get; set; }
    }
}

Using the Repository Pattern, we can decouple the AssignedPerson entities (FirstName, MiddleName, LastName, etc.) from any data access logic. The decoupling occurs through abstraction which encapsulates any knowledge of implementation written to store data. The repository class logic is written as such that allows us to decouple our classes from the associated dependencies facilitating modification with minimal effect to other classes. In addition, the concrete implementation of any class that holds dependency to another class is not known at compile time. The service locator is used as a central reference repository – referencing and locating instances. We then use these instances to perform CRUD operation on the data storage utilizing the repository pattern.

C#
// Here is the source code for the Repository

using System;
using System.Collections.Generic;
using System.Linq;
using KDatalayer.Contracts;
using KDatalayer.Entities;
using NHibernate.Linq;
using KCore;

namespace KDataLayer.Repositories
{
    /// <summary>
    /// Assigned Person's Repository
    /// </summary>
    public class AssignedPersonRepository
    {
        /// <summary>
        /// Get Person based on sId
        /// </summary>
        /// <param name="sId"></param>
        /// <returns></returns>
        public AssignedPerson Find(int sId)
        {
            var s = ServiceLocator.Resolve<IKSessionFactory>().OpenSession();
            var q = s.Query<AssignedPerson>()
                .Where(p => p.Id == sId)
                .ToList();
            if (q.Count() != 1)
            {
                // Do Something: like log warning
            }
            return q.SingleOrDefault();
        }
        /// <summary>
        /// Return Assigned Person based off filter
        /// </summary>
        /// <param name="filter"></param>
        /// <returns></returns>
        public IEnumerable<AssignedPerson> Query(Func<AssignedPerson, bool> filter)
        {
            var s = ServiceLocator.Resolve<IKSessionFactory>().OpenSession();
            var settings = s.Query<AssignedPerson>()
                .Where(filter)
                .ToList();
            return settings;
        }
        /// <summary>
        /// Save changes
        /// </summary>
        /// <param name="entities"></param>
        public void SaveAll(IEnumerable<AssignedPerson> entities)
        {
            var s = ServiceLocator.Resolve<IKSessionFactory>().OpenSession();
            var t = s.BeginTransaction();
            try
            {
                foreach (var item in entities)
                {
                    var cfg = item;
                    s.SaveOrUpdate(cfg);
                }
                t.Commit();
            }
            catch (Exception /*ex*/)
            {
                // Do Something: like log error
                t.Rollback();
            }
        }
        /// <summary>
        /// Delete Assigned User of corresponding sId
        /// </summary>
        /// <param name="sId"></param>
        public void Delete(int sId)
        {
            var s = ServiceLocator.Resolve<IKSessionFactory>().OpenSession();
            var t = s.BeginTransaction();
            try
            {
                var inbounds = s.Query<AssignedPerson>()
                .Where(p => p.Id == sId)
                .ToList();
                foreach (var inbound in inbounds)
                    s.Delete(inbound);
                t.Commit();
            }
            catch (Exception /*ex*/)
            {
               // Do Something: like log error
                t.Rollback();
            }
        }
    }
}

Putting it all Together (MVC 3 Controller and Views)

From the code written below you can see how the controller makes good use of the repository pattern traversing data through the entities. For the sake of longevity, I did not include the view but you can easily add the corresponding views into this project by right-clicking a controller action and then selecting the menu option Add View. The views are included in the demo solution provided for download.

C#
// Here is the source code for the Assigned Person model

using System;
using System.Linq;
using System.Web.Mvc;
using KDatalayer.Entities;
using KDataLayer.Repositories;
using KModel;

namespace Kodiak.Controllers
{
    public class AssignedPersonController : Controller
    {
        public ActionResult Index()
        {
            var entityList = new AssignedPersonRepository().Query(
                                   itm => true).OrderBy(p => p.AssignmentStart);
            var data = entityList.Select(e => new AssignedPersonModel
            {
                Id = e.Id,
                FirstName = e.FirstName,
                MiddleName = e.MiddleName,
                LastName = e.LastName,
                AssignmentStart = e.AssignmentStart,
                AssignmentEnd = e.AssignmentEnd,
                AssignmentId = e.Assignment_id,
                InstallationId = e.Installation_id
            });
            return View(data);
        }

        public ActionResult Details(int id)
        {
            var repo = new AssignedPersonRepository();
            var entity = repo.Find(id);

            var data = new AssignedPersonModel()
            {
                Id = entity.Id,
                FirstName = entity.FirstName,
                LastName = entity.LastName,
                MiddleName = entity.MiddleName,
                AssignmentStart = entity.AssignmentStart,
                AssignmentEnd = entity.AssignmentEnd,
                AssignmentId = entity.Assignment_id,
                InstallationId = entity.Installation_id
            };

            return View(data);
        }

        public ActionResult Create()
        {
            var data = new AssignedPersonModel
            {
                // create a new entry
            };
            return View(data);
        }

        [HttpPost]
        public ActionResult Create(AssignedPersonModel model)
        {
            try
            {
                var repo = new AssignedPersonRepository();
                var entity = new AssignedPerson()
                {
                    FirstName = model.FirstName,
                    LastName = model.LastName,
                    MiddleName = model.MiddleName,
                    AssignmentStart = model.AssignmentStart,
                    AssignmentEnd = model.AssignmentEnd,
                    Assignment_id = model.AssignmentId,
                    Installation_id = model.InstallationId
                };

                repo.SaveAll(new[] { entity });

                return RedirectToAction("Index");
            }
            catch (Exception /*ex*/)
            {
                //Do something with the error
                return View();
            }
        }

        public ActionResult Edit(int id)
        {
            var repo = new AssignedPersonRepository();
            var entity = repo.Find(id);

            var data = new AssignedPersonModel()
            {
                Id = entity.Id,
                FirstName = entity.FirstName,
                LastName = entity.LastName,
                MiddleName = entity.MiddleName,
                AssignmentStart = entity.AssignmentStart,
                AssignmentEnd = entity.AssignmentEnd,
                AssignmentId = entity.Assignment_id,
                InstallationId = entity.Installation_id
            };

            return View(data);
        }

        [HttpPost]
        public ActionResult Edit(int id, AssignedPersonModel model)
        {
            try
            {
                var repo = new AssignedPersonRepository();

                var entity = new AssignedPerson()
                {
                    Id = model.Id,
                    FirstName = model.FirstName,
                    LastName = model.LastName,
                    MiddleName = model.MiddleName,
                    AssignmentStart = model.AssignmentStart,
                    AssignmentEnd = model.AssignmentEnd,
                    Assignment_id = model.AssignmentId,
                    Installation_id = model.InstallationId
                };

                repo.SaveAll(new[] { entity });

                return RedirectToAction("Index");
            }
            catch
            {
                // Do something with the error
                return View(model);
            }
        }

        public ActionResult Delete(int id)
        {
            var repo = new AssignedPersonRepository();
            var entity = repo.Find(id);

            var data = new AssignedPersonModel()
            {
                Id = entity.Id,
                FirstName = entity.FirstName,
                LastName = entity.LastName,
                MiddleName = entity.MiddleName,
                AssignmentStart = entity.AssignmentStart,
                AssignmentEnd = entity.AssignmentEnd,
                AssignmentId = entity.Assignment_id,
                InstallationId = entity.Installation_id
            };

            return View(data);
        }

        [HttpPost]
        public ActionResult Delete(int id, AssignedPersonModel model)
        {
            try
            {
                var repo = new AssignedPersonRepository();
                repo.Delete(id);

                return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }
    }
}

Unit Testing

Below illustrates how we can easily test the repository query and find (lookup) methods and also evaluate if the Service Locator resolves the correct dependency to inject. In this case the resolution is the SessionFactory and if you remember we "registered" the SessionFactory dependency inside our LightCore.cfg file. The session factory has a significant amount of dependency behind it such as connection strings, mappings, and settings via Fluent NHibernate ORM configuration and, in addition, repository CRUD actions that also depend on the session. We can test the repository methods minimizing the impact on the session or vice versa. There are more unit test examples in the solution demo provided for download.

C#
// Here is the source code for the Systems Settings implementation

using System.Linq;
using NHibernate.Linq;
using KDatalayer.Entities;
using KDatalayer.Contracts;
using KDataLayer.Repositories;
using KModel;
using NUnit.Framework;
using KCore;

namespace KTests.Controller_Test
{
    [TestFixture()]
    public class AssignedPersonRepositoryTests
    {
        [Test()]  //Tests the assigned person Repository and Model
        public void Get_AssignedPerson()
        {
            var entityList = new AssignedPersonRepository().Query(
                                   itm => true).OrderBy(p => p.AssignmentStart);
            var data = entityList.Select(e => new AssignedPersonModel
            {
                Id = e.Id,
                FirstName = e.FirstName,
                MiddleName = e.MiddleName,
                LastName = e.LastName,
                AssignmentStart = e.AssignmentStart,
                AssignmentEnd = e.AssignmentEnd,
                AssignmentId = e.Assignment_id,
                InstallationId = e.Installation_id
            });

            Assert.That(entityList, Is.Not.Null);
            Assert.That(data, Is.Not.Null);
        }

        [Test()] //Test the Repository lookup retrieval
        public void Get_AssignedPerson1()
        {
            var gaPerson = new AssignedPersonRepository();
            var rec = gaPerson.Find(3);

            Assert.That(gaPerson, Is.Not.Null);
            Assert.That(rec, Is.Not.Null);
        }

        [Test()] // Test Repository with Service Locator and Session Factory
        public void Find()
        {
            var session = ServiceLocator.Resolve<iksessionfactory>().OpenSession();

            var perAssigned = session.Query<assignedperson>()
                .Where(p => p.Id == 1)
                .ToList();

            Assert.That(session, Is.Not.Null);
            Assert.That(perAssigned, Is.Not.Null);
            Assert.That(perAssigned.Count, Is.GreaterThan(0));
        }
    }
}

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
United States United States
Enabling the achievement of full potential through innovative research of advanced machine learning technologies to address the evolving strategic military battlespace technical hypothesis, leading to capabilities that will transition to operational prototypes for complex digital transformations.

Doctorate Computer Science

Comments and Discussions

 
QuestionAbout your unit testing Pin
Hardy Wang7-Dec-11 6:10
Hardy Wang7-Dec-11 6:10 
AnswerRe: About your unit testing Pin
Dr. Curtis Shull7-Dec-11 6:49
Dr. Curtis Shull7-Dec-11 6:49 
GeneralRe: About your unit testing Pin
Robert Slaney18-Dec-12 8:41
Robert Slaney18-Dec-12 8:41 
GeneralRe: About your unit testing Pin
Dr. Curtis Shull18-Dec-12 9:28
Dr. Curtis Shull18-Dec-12 9:28 
GeneralVery good! Pin
SelmaS11-Nov-11 9:52
SelmaS11-Nov-11 9:52 
QuestionI like this Pin
Sacha Barber12-Sep-11 22:20
Sacha Barber12-Sep-11 22:20 
GeneralRe: I like this Pin
jim lahey17-May-13 0:17
jim lahey17-May-13 0:17 
QuestionLiking the emphasis on "Lightweight" Pin
Mel Padden12-Sep-11 21:45
Mel Padden12-Sep-11 21:45 
QuestionRe: Inversion of Control/Dependency Injection with Repository Pattern, Fluent Nhibernate and LightCore Framework Pin
After20502-Sep-11 16:37
After20502-Sep-11 16:37 
SuggestionUsing AutoMapper and Service Layer Pin
Malik Hassan24-Aug-11 1:51
Malik Hassan24-Aug-11 1:51 
Hi Curtis Shull,

Thanks for your great post , it's really useful, I have the following suggestions:

1 - Rather than using manual mapping in the Get_AssignedPerson action between
C#
AssignedPerson 
and
C#
AssignedPersonModel
,you can use AutoMapper for this mapping , it will help you to convert AssignedPerson to AssignedPersonModel.

2- You are using the repository directly from the action , why not to provide a service layer that will hold any business logic and will be responsible on calling the repository , in your current implementation , the action will control any business logic and no one else.

Thanks
GeneralRe: Using AutoMapper and Service Layer [modified] Pin
Dr. Curtis Shull24-Aug-11 6:07
Dr. Curtis Shull24-Aug-11 6:07 
Question5 STAR Pin
Pasquale DiFlavis4-Aug-11 5:35
Pasquale DiFlavis4-Aug-11 5:35 
AnswerRe: 5 STAR Pin
Dr. Curtis Shull4-Aug-11 7:25
Dr. Curtis Shull4-Aug-11 7:25 
GeneralMy vote of 5 Pin
Stephen Brannan3-Aug-11 7:53
Stephen Brannan3-Aug-11 7:53 
GeneralRe: My vote of 5 Pin
Dr. Curtis Shull4-Aug-11 4:55
Dr. Curtis Shull4-Aug-11 4:55 
GeneralMy vote of 5 Pin
Isabel Furini3-Aug-11 7:03
Isabel Furini3-Aug-11 7:03 
GeneralRe: My vote of 5 Pin
Dr. Curtis Shull4-Aug-11 4:56
Dr. Curtis Shull4-Aug-11 4:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.