How to use Sharp Architecture and Contrib in Windows GUI and service applications - sharparchitecture/Sharp-Architecture-Contrib GitHub Wiki
The S#arp Architecture Contrib team believes that most developers using S#arp Architecture in non-web applications want to leverage work already done in a web application. For example, you might need a web service to run background processing tasks for your web application. Therefore, this how-to focuses on adding a Windows application to an existing solution that already has a working web. If you don't have a working web yet, you can use the solution template that comes with S#arp Architecture to create one.
Note: Make sure you're using the latest Contrib from here.
Your project must configure NHibernate and Castle Windsor at startup. This tutorial guides to use ThreadSessionStorage with non-web projects.
-
NHibernate Configuration File:
Add a link to your web's NHibernate.config to your Windows application. If you are unsure how to do this, check out the information on adding an item as a link in this Microsoft article How to: Add Existing Items to a Project. -
Add References:
If you're getting a bunch of "Database not configured through the database method" and going crazy because everything is configured correctly, you forgot this step. Make sure the following are referenced in your project:Castle.Core Castle.Windsor
Visual Studio will not add all these automatically. I don't known if all these are necessary so if you're really trying to save on references, you might want to eliminate them one by one.
-
Move .Web.CastleWindsor.ComponentRegistrar to a Shared DLL .Web.CastleWindsor.ComponentRegistrar will need to be accessed by your Windows application. Of course, you cannot reference your web DLL from your Windows application so it will have to be moved to a common location. Arguably, this is an application service and can be moved to .ApplicationServices.CastleWindsor.
-
Create a Static Class to Initialize Service Locator Add the following class to .ApplicationServices.CastleWindsor:
using Castle.Windsor; using CommonServiceLocator.WindsorAdapter; using Microsoft.Practices.ServiceLocation; using SharpArchContrib.Castle.CastleWindsor; namespace <Your Project>.ApplicationServices.CastleWindsor { public static class ServiceLocatorInitializer { public static void Init() { IWindsorContainer container = new WindsorContainer(); //Register all the Contrib Components ComponentRegistrar.AddComponentsTo(container); ServiceLocator.SetLocatorProvider(() => new WindsorServiceLocator(container)); } } }
-
Create a Static Class to Initialize NHibernate
Add the following class to .Data:namespace <Your Project>.Data { public static class Initializer { public static void Init() { NHibernateSession.Init(new ThreadSessionStorage(), new[] {"<Your Project>.Data.dll"}, new AutoPersistenceModelGenerator().Generate(), "NHibernate.config"); } } }
Notice that this configuration is using ThreadSessionStorage, which is required if you will be using the UnitOfWork support. Transaction attribute can be used with any session storage.
-
Initialize Service Location and NHibernate When Application Starts Make sure to call .ApplicationServices.CastleWindsor.ServiceLocatorInitializer.Init() followed by .Data.Initializer.Init() when your application starts.
Important!: Make sure you register the components from SharpArchContrib before registering any components from your project. Failing to do this will result in castle not recognizing the Transaction or UnitOfWork attributes since the interceptor has not been registered yet.
For best results you should attribute any method that performs NHibernate operations with the UnitOfWork attribute. The UnitOfWork attribute will ensure that your application adheres to best-practices when using NHibernate. Review the documentation on UnitOfWork for more information.
A lot of questions about implementing a simple scheduled job seem to show a knowledge gap in how Castle Windsor works. I really suggest going through Castle Windsor's documentation if you haven't already. If you have a few very simple tasks that need be done on a recurring basis, please look at the console application below.
I think it bears repeating that controllers are meant for orchestration and in a well developed application, it would be better to call an "application service" than a controller from a scheduled task. Nonetheless, this will get you going. Compile the application and attach it to Task Scheduler in Windows for a poor man's cron job.
namespace Northwind.Console
{
using System;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using CommonServiceLocator.WindsorAdapter;
using Microsoft.Practices.ServiceLocation;
using Core;
using Data;
using SharpArch.Core.PersistenceSupport;
using SharpArch.Core.PersistenceSupport.NHibernate;
using SharpArch.Data.NHibernate;
using SharpArchContrib.Castle.CastleWindsor;
public class Program
{
private static IWindsorContainer container;
public static void Main(string[] args)
{
Console.WriteLine("Starting up");
BootStrapper();
ServiceJob();
Console.WriteLine("All done");
// Console.ReadLine();
}
public static void BootStrapper()
{
container = new WindsorContainer();
ComponentRegistrar.AddComponentsTo(container);
container.Register(
Component
.For(typeof(ISessionFactoryKeyProvider))
.ImplementedBy(typeof(DefaultSessionFactoryKeyProvider))
.Named("sessionFactoryKeyProvider"));
container.Register(
Component
.For(typeof(IEntityDuplicateChecker))
.ImplementedBy(typeof(EntityDuplicateChecker))
.Named("entityDuplicateChecker"));
container.Register(
Component
.For(typeof(IRepository<>))
.ImplementedBy(typeof(Repository<>))
.Named("repositoryType"));
container.Register(
Component
.For(typeof(INHibernateRepository<>))
.ImplementedBy(typeof(NHibernateRepository<>))
.Named("nhibernateRepositoryType"));
container.Register(
Component
.For(typeof(IRepositoryWithTypedId<,>))
.ImplementedBy(typeof(RepositoryWithTypedId<,>))
.Named("repositoryWithTypedId"));
container.Register(
Component
.For(typeof(INHibernateRepositoryWithTypedId<,>))
.ImplementedBy(typeof(NHibernateRepositoryWithTypedId<,>))
.Named("nhibernateRepositoryWithTypedId"));
ServiceLocator.SetLocatorProvider(() => new WindsorServiceLocator(container));
// Since this just looks for the assembly, leave it be.
Initializer.Init();
}
public static void ServiceJob()
{
var productRepository = container.Resolve<IRepository<Product>>();
var productCount = productRepository.GetAll().Count;
Console.WriteLine("Complete, # of products: " + productCount);
}
}
}