Can I pass a parameter to ISessionFactory in NHibernate? - c#

I am very new (i.e. an hour or so) to NHibernate. I've followed a tutorial which gave me the following class:
public class ContactNHibernateHelper
{
private static ISessionFactory _sessionFactory;
private static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
{
var configuration = new Configuration();
configuration.Configure();
configuration.AddAssembly(typeof (CRMData.Objects.Contact).Assembly);
_sessionFactory = configuration.Configure().BuildSessionFactory();
}
return _sessionFactory;
}
}
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
}
In extending my application, I now have another class for a different object. I'm trying to rewrite the above class so that I can pass in the assembly type as a parameter and return the _sessionFactory. So, for example, I would have a variable passed in to the method called assembly. The code would then be:
public class GenericNHibernateHelper
{
private static ISessionFactory _sessionFactory;
private static ISessionFactory SessionFactory(System.Reflection.Assembly assembly)
{
get
{
if (_sessionFactory == null)
{
var configuration = new Configuration();
configuration.Configure();
configuration.AddAssembly(assembly);
_sessionFactory = configuration.Configure().BuildSessionFactory();
}
return _sessionFactory;
}
}
}
This is giving the error 'Cannot resolve symbol 'get'' - presumably because I cannot pass any parameters in this way.
I am probably missing something very simple - any ideas?

You don't need to make any changes if your other class is in the same assembly as CRMData.Objects.Contact.
But if you want to pass in a parameter you need to convert the SessionFactory property to a method or create a constructor that accepts the parameter.
private static ISessionFactory SessionFactory(System.Reflection.Assembly assembly)
{
if (_sessionFactory == null)
{
var configuration = new Configuration();
configuration.Configure();
configuration.AddAssembly(assembly);
_sessionFactory = configuration.Configure().BuildSessionFactory();
}
return _sessionFactory;
}

we dont have parameter properties in C# other than indexers.That is the reason you are getting the error.change your code to use a method.
But i still dont understand why you need to pass an assembly to the method.

Related

Using Unity IoC in LINQPad

I'm very new to using Unity.
I'm trying to test a segment of code in LINQPad. This code uses a DBContext which relies on Log4Net as a service. I'm trying to write the sample code to use the actual DBContext, but can't get it to construct.
The code I'm trying is below. If there is more information needed, please ask for it before down-voting, since I'm not sure how much you need to see to understand my issue, since I'm still learning Unity.
void Main()
{
RegisterObjects();
var logger = IoCHelper.Resolve<ILogger>();
//var _logger = new Clark.Logging.MultiLogger();
var _logger = logger;
var _ediContext = new EdiContext();
var transactionId = 1008;
var limit = 0;
var temp = new Type210SubscriberProvider(_ediContext)
.GetAfterNew(transactionId, limit);
temp.Dump();
var temp2 = new Type210SubscriberProvider(_ediContext)
.GetAfter(transactionId, limit);
temp2.Dump();
}
public void RegisterObjects()
{
XmlConfigurator.Configure();
var multiLogger = new MultiLogger();
multiLogger.Register(new Log4NetLogger());
IoCHelper.RegisterInstance<ILogger>(multiLogger);
}
If I try just this:
void Main()
{
var _ediContext = new EdiContext();
}
The error message I am receiving in LINQPad is:
Resolution of the dependency failed, type = "Clark.Logging.ILogger", name = "(none)".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The current type, Clark.Logging.ILogger, is an interface and cannot be constructed. Are you missing a type mapping?
At the time of the exception, the container was:
Resolving Clark.Logging.ILogger,(none)
UPDATE:
Here is some more detail from the Global.asax.cs file. I'm not sure how to translate this to LINQPad.
protected void Application_Start()
{
// GlobalConfiguration.Configuration is an HttpConfiguration object.
ConfigureContainer(GlobalConfiguration.Configuration);
ConfigureServices(GlobalConfiguration.Configuration);
}
private static void ConfigureServices(HttpConfiguration configuration)
{
configuration.Services.Add(typeof (IExceptionLogger), new UnhandledExceptionLogger(GetLogger()));
configuration.Services.Replace(typeof (IExceptionHandler), new UnhandledExceptionHandler());
}
private static void ConfigureContainer(HttpConfiguration config)
{
config.DependencyResolver = new IoCContainer(IoCHelper.Container);
new LoggingDependencyInitializer().RegisterObjects();
IoCHelper.RegisterType<IEdiContext>(new InjectionFactory(unityContainer => new EdiContext()));
IoCHelper.RegisterType<SubscriberController>();
IoCHelper.RegisterType<ConsumerInformationController>();
IoCHelper.RegisterType<TransactionTypeController>();
}
private static ILogger GetLogger()
{
return IoCHelper.Resolve<ILogger>();
}
Here is the IoCHelper class:
public static class IoCHelper
{
private static UnityContainer _container;
public static UnityContainer Container
{
get { return _container ?? (_container = new UnityContainer()); }
}
public static T Resolve<T>()
{
return Container.Resolve<T>();
}
public static void RegisterType<TFrom, TTo>() where TTo : TFrom
{
Container.RegisterType<TFrom, TTo>();
}
public static void RegisterInstance(Type type, object instance)
{
Container.RegisterInstance(type, instance);
}
public static void RegisterType<T>()
{
Container.RegisterType<T>();
}
public static void RegisterInstance<T>(T instance)
{
Container.RegisterInstance(instance);
}
public static void RegisterType<T>(InjectionFactory injectionFactory)
{
Container.RegisterType<T>(injectionFactory);
}
}
When using an IOC container, you need to register your types (e.g. map interfaces \ abstract classes into their real types). Since when you want the container to resolve an interface, it will want to find it's mapping before providing the instance.
BTW, if Unity doesn't find a map, it will try to construct the type. In your case it fails since you cannot construct an interface.
With Unity, you can do it either by configuration using:
var section = ConfigurationManager.GetSection(SectionName) as UnityConfigurationSection;
if (section != null)
{
section.Configure(container);
}
Or directly:
container.RegisterType<InterfaceType, ConcreteType>();
Since I see this code line in your example:
IoCHelper.RegisterInstance<ILogger>(multiLogger);
You are registering ILogger with a specific instance. There must be a problem with your IoCHelper implementation. Please add more code and I'll edit my answer with a specific solution.

Setting up Nhibernate mapping by code session factory with MySQL

I am trying to setup a Session factory with Nhibernate code by mapping, but I have issues configuring it, and its pretty hard to find guides to Code by mapping with the session factory.
Atm. I have this SessionManager, but I am uncertain where to specify its a MySQL database, proberly miss more.
public class SessionManager
{
private const string ConnString = "Server=localhost; Port=3306; Database=test; Uid=root; Pwd=123456;";
public static SessionManager CurrentInstance
{
get
{
if (_currentInstance == null)
{
object sync = new object();
lock (sync)
_currentInstance = new SessionManager();
}
return _currentInstance;
}
}
public static ISession Session
{
get
{
if (_sessionFactory == null)
{
object sync = new object();
lock (sync)
_sessionFactory = new Configuration()
.DataBaseIntegration(x => x.ConnectionString = ConnString)
.Configure()
.AddAssembly(typeof(EmployeeMap).Assembly)
.BuildSessionFactory();
}
return _sessionFactory.OpenSession();
}
}
private SessionManager() { }
static SessionManager _currentInstance;
static ISessionFactory _sessionFactory;
}
I think what you are trying to do is specify that you are using mysql. When I have done this I have used an NHibernate confiuration file with a line stating the driver_class:
<property name="connection.driver_class">NHibernate.Driver.MySqlDataDriver</property>
Try this tutorial
http://nhforge.org/wikis/howtonh/your-first-nhibernate-based-application.aspx
it describes the process of setting up your nhibernate session and shows a sample hibernate.cfg.xml file. In this set up you need to specify the MySqlDataDriver instead of the SQLServerCeDriver shown.
I don't know if you can do this without having to use a hibernate.cfg.xml

IAutoMappingOverride.Override not being called

I can't seem to get the FluentNHibernate overrides to run. Here's my configuration method
private static void Configure()
{
if (_configuration != null) return;
_configuration = new Configuration();
_configuration.Configure();
_configuration
.AddAssembly(typeof(IFoo).Assembly)
.AddAssembly(OtherDataAssembly);
var autoPersistenceModel = AutoMap
.AssemblyOf<IFoo>()
.AddEntityAssembly(OtherDataAssembly)
.Conventions.AddAssembly(OtherDataAssembly)
.Conventions.Add(DefaultCascade.None())
.UseOverridesFromAssemblyOf<IFoo>()
.UseOverridesFromAssembly(OtherDataAssembly)
.OverrideAll(map => map.IgnoreProperty("IsIgnored"))
.Where(IsTypeMatch);
_sessionFactory = Fluently
.Configure(Configuration)
.Mappings(m => m.AutoMappings
.Add(autoPersistenceModel))
.BuildSessionFactory();
}
Here's my override class
public class FooOverride : IAutoMappingOverride<IFoo>
{
public void Override(AutoMapping<IFoo> mapping)
{
mapping.Not.LazyLoad();
mapping.HasManyToMany(x => x.Bar).Table("FooBar");
}
}
Breakpoints I put in the Override() method are never hit when debugging, even when restarting IIS. How do I get this to run?
Overrides are only run on the exact same class, not classes which are assignable to the class in the generic type parameter of IAutoMappingOverride.
Update:
The only other options i can think of now are:
let FooOverride implement several IAuotMappingOverride<> for each class implementing IFoo
add the overides yourself using Reflection. untestet:
var overrideMethod = typeof(AutoPersistenceModel).GetMethod("Override");
foreach (var type in typeof(IFoo).Assembly)
{
if (typeof(IFoo).IsAssignableFrom(type))
{
overrideMethod.MakeGenericMethod(type).Invoke(new Action<IFoo>(m => m.HasMayToMany(...)));
}
}

Abstract factories when using dependency injection frameworks

I'm wondering how to properly use abstract factories when using a DI framework and one of the parameters in that factory is a dependency that should be handled by the DI framework.
I am not sure whether to make my abstract factory omit the parameter completely then use my DI container to wire it up or whether I should pass the dependency to the object.
For example, I have a TcpServer and it uses a Session.Factory to create sockets. The Session object actually takes a Processor in its constructor. Should I pass the Processor to the TcpServer then have it pass it onto the Session.Factory or have my DI container do the wiring?
If I were to have the DI container do the wiring it would look like this:
class Session : ISession
{
public delegate ISession Factory(string name);
...
public Session(string name, Processor processor)
{
...
}
}
class TcpServer : ITcpServer
{
private readonly Session.Factory _sessionFactory;
public TcpServer(Session.Factory sessionFactory)
{
this._sessionFactory = socketFactory;
}
...
public void OnConnectionReceived()
{
...
var session= _sessionFactory(ip.LocalEndPoint());
...
}
}
Then using a DI container like Ninject I'd be able to do this when configuring the container:
Bind<Session.Factory>().ToMethod(c =>
{
var processor = Kernel.Get<Processor>();
return (name) => new Session(name, processor);
}).InSingletonScope();
My main issue with this approach is that it assumes whoever creates the Session.Factory knows about the processor. In my case, since I am using a DI container, this is actually very convenient but it seems weird to have a factory have its own dependencies. I always imagined a factory not really ever having any members.
If I were to pass the dependency through
class Session : ISession
{
public delegate ISession Factory(string name, Processor processor);
...
public Session(string name, Processor processor)
{
...
}
}
class TcpServer : ITcpServer
{
private readonly Session.Factory _sessionFactory;
private readonly Processor _processor;
public TcpServer(Session.Factory sessionFactory, Processor processor)
{
this._processor = processor;
}
...
public void OnConnectionReceived()
{
...
var session = _sessionFactory(ip.LocalEndPoint(), _processor);
...
}
}
I have two issues with the second approach:
The TcpServer doesn't actually do anything with the Processor. It just passes it along. Seems like this is poor man's DI at work almost.
In the real program behind this code, the Processor actually has a reference to the TcpServer. Therefore when using this approach, I get a circular reference. When I break it apart by using the first scenario then it's not an issue.
What do you think is the best approach? I am open to new ideas as well.
Thanks!
Many containers support factories in one or another way and this is the way you should go.
E.g. Taking your example define a ISessionFactory interface like this
public interface ISessionFactory
{
ISession CreateSession(string name);
}
For Ninject 2.3 see https://github.com/ninject/ninject.extensions.factory and let it be implemented by Ninject
Bind<ISessionFactory>().AsFactory();
For 2.2 do the implementation yourself
public class SessionFactory : ISessionFactory
{
private IKernel kernel;
public SessionFactory(IKernel kernel)
{
this.kernel = kernel;
}
public ISession CreateSession(string name)
{
return this.kernel.Get<ISession>(new ConstructorArgument("name", name));
}
}
The pattern I use for an abstract factory pattern is a little different from yours. I use something like setter injection on a generic singleton, but wrap the configurable delegate "property" in a more intuitive interface.
I would prefer not to have to register each implementation individually, so I would prefer to use some convention that can be tested at application start up. I'm not sure about the Ninject syntax for autoregistering custom conventions, but the logic would come down to scanning the relevant assemblies for reference types, T, that have static readonly fields of type AbstractFactory<T>, then calling Configure(Func<T>) on that static member using reflection.
An example of the generic abstract factory singleton and how it would be declared on a Session is below.
public class Session {
public static readonly AbstractFactory<Session> Factory = AbstractFactory<Session>.GetInstance();
}
public sealed class AbstractFactory<T>
where T: class{
static AbstractFactory(){
Bolt = new object();
}
private static readonly object Bolt;
private static AbstractFactory<T> Instance;
public static AbstractFactory<T> GetInstance(){
if(Instance == null){
lock(Bolt){
if(Instance == null)
Instance = new AbstractFactory<T>();
}
}
return Instance;
}
private AbstractFactory(){}
private Func<T> m_FactoryMethod;
public void Configure(Func<T> factoryMethod){
m_FactoryMethod = factoryMethod;
}
public T Create() {
if(m_FactoryMethod == null) {
throw new NotImplementedException();
}
return m_FactoryMethod.Invoke();
}
}
Update
If you need to pass parameters into your factory method, then you can alter the class such as:
public sealed class AbstractFactory<TDataContract,T>
where T: class{
static AbstractFactory(){
Bolt = new object();
}
private static readonly object Bolt;
private static AbstractFactory<TDataContract,T> Instance;
public static AbstractFactory<TDataContract,T> GetInstance(){
if(Instance == null){
lock(Bolt){
if(Instance == null)
Instance = new AbstractFactory<T>();
}
}
return Instance;
}
private AbstractFactory(){}
private Func<TDataContract,T> m_FactoryMethod;
public void Configure(Func<TDataContract,T> factoryMethod){
m_FactoryMethod = factoryMethod;
}
public T Create(TDataContract data) {
if(m_FactoryMethod == null) {
throw new NotImplementedException();
}
return m_FactoryMethod.Invoke(data);
}
}
Your SessionData, Session and TcpServer might look like
public class SessionData{
public DateTime Start { get; set; }
public string IpAddress { get; set; }
}
public class Session {
public static readonly AbstractFactory<SessionData,Session> Factory = AbstractFactory<Session>.GetInstance();
private readonly string _ip;
private readonly DateTime _start;
public Session(SessionData data) {
_ip = data.IpAddress;
_start = DateTime.Now;
}
public event EventHandler<RequestReceivedEventEventArgs> RequestAdded;
}
public class RequestReceivedEventArgs: EventArgs {
public SessionRequest Request { get; set; }
}
public class TcpServer : ITcpServer
{
private readonly Processor _processor;
public TcpServer(Processor processor)
{
this._processor = processor;
}
public void OnConnectionReceived()
{
var sessionData = new SessionData {
IpAddress = ip.LocalEndPoint(),
Start = DateTime.Now
};
var session = Session.Factory.Create(sessionData);
//...do other stuff
}
public void ServeResponse(SessionRequest request){
_processor.Process(request);
}
}
When configuring your DI container, you can set up the factory such as:
Session.Factory.Configure(sessionData => {
// instead of injecting the processor into the Session, configure events
// that allow the TcpServer to process the data.
// (After all, it is more logical for a servers to serve a request than
// it is for a Session to do the Processing. Session's tend to store data
// and state, not invoke processes
session.RequestAdded += (sender,e) => {
Kernel.Get<ITcpServer>.ServeResponse(e.Request);
};
});

Nhibernate session management strategy for web application with background-workers?

For a web application, it seems like a good way to handle the session is to use the setting <property name="current_session_context_class">managed_web</property>, call CurrentSessionContext.Bind/Unbind on Begin/EndRequest. Then I can just use sessionFactory.GetCurrentSession() in the repository class.
This works fine for all page request. But I have background workers doing stuff and using the same repository classes to do stuff. These do not run within a web request, so that session handling won't work.
Any suggestions to how this can be solved?
I solved it by creating my own session context class:
public class HybridWebSessionContext : CurrentSessionContext
{
private const string _itemsKey = "HybridWebSessionContext";
[ThreadStatic] private static ISession _threadSession;
// This constructor should be kept, otherwise NHibernate will fail to create an instance of this class.
public HybridWebSessionContext(ISessionFactoryImplementor factory)
{
}
protected override ISession Session
{
get
{
var currentContext = ReflectiveHttpContext.HttpContextCurrentGetter();
if (currentContext != null)
{
var items = ReflectiveHttpContext.HttpContextItemsGetter(currentContext);
var session = items[_itemsKey] as ISession;
if (session != null)
{
return session;
}
}
return _threadSession;
}
set
{
var currentContext = ReflectiveHttpContext.HttpContextCurrentGetter();
if (currentContext != null)
{
var items = ReflectiveHttpContext.HttpContextItemsGetter(currentContext);
items[_itemsKey] = value;
return;
}
_threadSession = value;
}
}
}
I've found it simplest in this scenario to handle session creation myself using a DI library and 'hybrid' scope (in StructureMap, this is defined as InstanceScope.Hybrid). This will scope instances by HttpContext in an ASP.net app domain, and ThreadStatic in a normal app domain, allowing you to use the same approach in both.
I'm sure other DI libraries offer a similar feature.
On my project, I wrote a little wrapper class around the CurrentSessionContext.
Perhaps you can extend it to suit your needs.
I think you just need to tweak the implementation of BindSessionToRequest and GetCurrentSession:
public static class SessionManager
{
private static ISessionFactory _sessionFactory = null;
private static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
{
//check whether we're in web context or win context, and create the session factory accordingly.
if (System.Web.HttpContext.Current != null)
{
if (_sessionFactory == null)
{
_sessionFactory = DAOBase.GetSessionFactory();
}
}
else
{
_sessionFactory = DAOBase.GetSessionFactoryForWin();
}
}
return _sessionFactory;
}
}
public static void BindSessionToRequest()
{
ISession session = SessionManager.SessionFactory.OpenSession();
NHibernate.Context.CurrentSessionContext.Bind(session);
}
public static bool CurrentSessionExists()
{
return NHibernate.Context.CurrentSessionContext.HasBind(SessionFactory);
}
public static void UnbindSession()
{
ISession session = NHibernate.Context.CurrentSessionContext.Unbind(SessionManager.SessionFactory);
if (session != null && session.IsOpen)
{
session.Close();
}
}
public static ISession GetCurrentSession()
{
return SessionFactory.GetCurrentSession();
}
}

Categories

Resources