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.
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.
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.
Select Internet Application and ASPX as the engine. Leave Unit Test unselected as we will add custom unit tests later.
Your Solution Explorer should look similar to this:
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:
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:
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):
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:
using System;
using System.Collections.Generic;
using System.IO;
namespace KCore.Contracts
{
interface ISystemSettings
{
string KodiakConnectionString { get; }
}
}
Add a new class in the Settings folder of the KCore project and insert the following code:
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:
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:
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.
using LightCore;
using LightCore.Configuration;
using LightCore.Registration;
namespace KCore
{
public static class ServiceLocator
{
private static IContainer _container;
public static T Resolve<T>()
{
if (_container == null) Configure("LightCore.config");
return (T)_container.Resolve(typeof(T));
}
public static void Configure(string filename)
{
if (_container != null)
{
}
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.
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.
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
{
public class SessionFactory : ISessionFactory
{
private readonly ISystemSettings _settings;
private readonly ISessionFactory _sessionFactory;
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.
<!---->
<?xml version="1.0" encoding="utf-8" ?>
<LightCoreConfiguration
xmlns="clr-namespace:LightCore.Configuration;assembly=LightCore.Configuration">
<LightCoreConfiguration.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:
You will need to set the LightCore.config file path here:
<!---->
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.
USE [Your Database Here]
GO
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.
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.
using System;
namespace KDatalayer.Entities
{
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.
using System;
namespace KModel
{
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.
using System;
using System.Collections.Generic;
using System.Linq;
using KDatalayer.Contracts;
using KDatalayer.Entities;
using NHibernate.Linq;
using KCore;
namespace KDataLayer.Repositories
{
public class AssignedPersonRepository
{
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)
{
}
return q.SingleOrDefault();
}
public IEnumerable<AssignedPerson> Query(Func<AssignedPerson, bool> filter)
{
var s = ServiceLocator.Resolve<IKSessionFactory>().OpenSession();
var settings = s.Query<AssignedPerson>()
.Where(filter)
.ToList();
return settings;
}
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 )
{
t.Rollback();
}
}
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 )
{
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.
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
{
};
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 )
{
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
{
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.
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()] 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()] 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()] 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));
}
}
}