I'm trying to inject dependencies via Spring.NET.
First I created a custom DependencyResolver:
public class SignalRSpringNetDependencyResolver : DefaultDependencyResolver
{
private IApplicationContext _context;
public SignalRSpringNetDependencyResolver(IApplicationContext context)
{
_context = context;
}
/// <summary>
/// Gets the application context.
/// </summary>
/// <value>The application context.</value>
public IApplicationContext ApplicationContext
{
get
{
if (_context == null || _context.Name != ApplicationContextName)
{
if (string.IsNullOrEmpty(ApplicationContextName))
{
_context = ContextRegistry.GetContext();
}
else
{
_context = ContextRegistry.GetContext(ApplicationContextName);
}
}
return _context;
}
}
/// <summary>
/// Gets or sets the name of the application context.
/// </summary>
/// <remarks>
/// Defaults to using the root (default) Application Context.
/// </remarks>
/// <value>The name of the application context.</value>
public static string ApplicationContextName { get; set; }
/// <summary>
/// Resolves singly registered services that support arbitrary object creation.
/// </summary>
/// <param name="serviceType">The type of the requested service or object.</param>
/// <returns>The requested service or object.</returns>
public override object GetService(Type serviceType)
{
System.Diagnostics.Debug.WriteLine(serviceType.FullName);
if (serviceType != null && !serviceType.IsAbstract && !serviceType.IsInterface && serviceType.IsClass)
{
var services = ApplicationContext.GetObjectsOfType(serviceType).GetEnumerator();
services.MoveNext();
try
{
return services.Value;
}
catch (InvalidOperationException)
{
return null;
}
}
else
{
return base.GetService(serviceType);
}
}
/// <summary>
/// Resolves multiply registered services.
/// </summary>
/// <param name="serviceType">The type of the requested services.</param>
/// <returns>The requested services.</returns>
public override IEnumerable<object> GetServices(Type serviceType)
{
var services = ApplicationContext.GetObjectsOfType(serviceType).Cast<object>();
services.Concat(base.GetServices(serviceType));
return services;
}
Note that i escape interfaces and abstract classes so that I will get the default implementations of SignalR from the base DefaultDependencyResolver
and here I assigned the resolver using WebActivator:
public static void PostStart()
{
// Inject Dependencies to SignalR, should be always come before ASP.NET MVC configuration
var dependecyResolver = new SignalRSpringNetDependencyResolver(ContextRegistry.GetContext());
GlobalHost.DependencyResolver = dependecyResolver;
RouteTable.Routes.MapHubs();
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
However, SignalR is always trying to resolve it's own dependencies using the Resolver i assigned and i get the following error:
'myhub' hub could not be resolved.
I only need the resolver to be aware of other dependencies(my Repository for example) and keep the default implementation of SignalR services.
I think it's hard to get Spring.Net working with SignalR
for the current version (Spring.Net 1.3.2) it's difficult to support asynchronous programming. Spring.Net session management doesn't play well with Task<T> types.
I ended up injecting my dependencies in 2 steps:
1- registering the required type on WebActivator PostStart:
GlobalHost.DependencyResolver.Register(
typeof(IUserService),
() => (UserService)ctx.GetObject("UserService"))
2- picking them up in my Hub constructor:
public MyHub()
{
_userService =
DependencyResolver.Current.GetService<IUserService>();
}
Related
I want to decorate my services with attributes for interception, and then have conventions based binding set the interceptors up for me. I don't want my attributes to inherit from the interception attributes... if I can avoid it.
For example, I have the following class:
[Log]
public class SomeClassToLog
{
public void DoSomething() { ... }
}
I understand I can bind this as follows:
var kernel = new StandardKernel();
kernel.Bind(x => x.FromAssembliesMatching("SomeProject.*")
.SelectAllClasses()
.WithAttribute(typeof(LogAttribute))
.BindToSelf().Configure(syntax => syntax.Intercept().With(LogInterceptor)));
How can I do this with different combinations of attributes and interceptors? For example:
If I have Log and Authorize attributes I would have to configure 3 sets of bindings? (1 for log without authorize, 1 for authorize without log and one for both log and authorize).
Updated: While I couldn't find a solution based on my original question parameters, I did stumble upon a similar question which lead me to the solution I ended up going with. Here is the source code:
Notes: Common.Interception.Interceptors namespace is in an assembly which has a reference to Ninject.Extensions.Interception (and all of its required dependencies). My attributes are defined in a separate assembly with no dependencies of their own.
MiscExtensions.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Common.Interception.Interceptors
{
// TODO: Remove the dependence on these eventually
/// <summary>
/// A bundle of extension methods which I needed to borrow from ninject source since they were internal:
/// Ninject.Extensions.Interception.Infrastructure.Language
/// ExtensionsForIEnumerable
/// ExtensionsForMethodInfo
/// ExtensionsForICustomAttributeProvider
/// </summary>
internal static class MiscExtensions
{
/// <summary>
/// Converts all of the items in the specified series using the specified converter.
/// </summary>
/// <typeparam name="TInput">The type of items contained in the input list.</typeparam>
/// <typeparam name="TOutput">The type of items to return.</typeparam>
/// <param name="items">The series of items to convert.</param>
/// <param name="converter">The converter to use to convert the items.</param>
/// <returns>A list of the converted items.</returns>
public static IEnumerable<TOutput> Convert<TInput, TOutput>(this IEnumerable<TInput> items,
Func<TInput, TOutput> converter)
{
return items.Select(converter);
}
/// <summary>
/// Skips the last items where the count of skipped items is given by count.
/// </summary>
/// <typeparam name="T">The type of the enumerable.</typeparam>
/// <param name="source">The source.</param>
/// <param name="count">The count of skipped items.</param>
/// <returns>An enumerable that skippes the last items from the source enumerable.</returns>
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int count)
{
var enumerator = source.GetEnumerator();
var items = new Queue<T>();
while (enumerator.MoveNext())
{
if (count-- <= 0)
{
yield return items.Dequeue();
}
items.Enqueue(enumerator.Current);
}
}
private const BindingFlags DefaultBindingFlags =
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance;
public static PropertyInfo GetPropertyFromMethod(this MethodInfo method, Type implementingType)
{
if (!method.IsSpecialName)
{
return null;
}
var isGetMethod = method.Name.Substring(0, 3) == "get";
var returnType = isGetMethod ? method.ReturnType : method.GetParameterTypes().Last();
var indexerTypes = isGetMethod ? method.GetParameterTypes() : method.GetParameterTypes().SkipLast(1);
return implementingType.GetProperty(method.Name.Substring(4), DefaultBindingFlags, null, returnType, indexerTypes.ToArray(), null);
}
public static PropertyInfo GetPropertyFromMethod(this MethodInfo method)
{
if (!method.IsSpecialName)
{
return null;
}
return method.DeclaringType.GetProperty(method.Name.Substring(4), DefaultBindingFlags);
}
/// <summary>
/// Gets the types of the parameters of the method.
/// </summary>
/// <param name="method">The method in question.</param>
/// <returns>An array containing the types of the method's parameters.</returns>
public static IEnumerable<Type> GetParameterTypes(this MethodBase method)
{
return method.GetParameters().Convert(p => p.ParameterType);
}
/// <summary>
/// Gets the method handle of either the method or its generic type definition, if it is
/// a generic method.
/// </summary>
/// <param name="method">The method in question.</param>
/// <returns>The runtime method handle for the method or its generic type definition.</returns>
public static RuntimeMethodHandle GetMethodHandle(this MethodBase method)
{
var mi = method as MethodInfo;
if (mi != null &&
mi.IsGenericMethod)
{
return mi.GetGenericMethodDefinition().MethodHandle;
}
return method.MethodHandle;
}
/// <summary>
/// Gets the first attribute of a specified type that decorates the member.
/// </summary>
/// <typeparam name="T">The type of attribute to search for.</typeparam>
/// <param name="member">The member to examine.</param>
/// <returns>The first attribute matching the specified type.</returns>
public static T GetOneAttribute<T>(this ICustomAttributeProvider member)
where T : Attribute
{
var attributes = member.GetCustomAttributes(typeof(T), true) as T[];
return (attributes == null) ||
(attributes.Length == 0)
? null
: attributes[0];
}
/// <summary>
/// Gets the first attribute of a specified type that decorates the member.
/// </summary>
/// <param name="member">The member to examine.</param>
/// <param name="type">The type of attribute to search for.</param>
/// <returns>The first attribute matching the specified type.</returns>
public static object GetOneAttribute(this ICustomAttributeProvider member, Type type)
{
object[] attributes = member.GetCustomAttributes(type, true);
return (attributes == null) ||
(attributes.Length == 0)
? null
: attributes[0];
}
/// <summary>
/// Gets an array of attributes matching the specified type that decorate the member.
/// </summary>
/// <typeparam name="T">The type of attribute to search for.</typeparam>
/// <param name="member">The member to examine.</param>
/// <returns>An array of attributes matching the specified type.</returns>
public static T[] GetAllAttributes<T>(this ICustomAttributeProvider member)
where T : Attribute
{
return member.GetCustomAttributes(typeof(T), true) as T[];
}
/// <summary>
/// Gets an array of attributes matching the specified type that decorate the member.
/// </summary>
/// <param name="member">The member to examine.</param>
/// <param name="type">The type of attribute to search for.</param>
/// <returns>An array of attributes matching the specified type.</returns>
public static object[] GetAllAttributes(this ICustomAttributeProvider member, Type type)
{
return member.GetCustomAttributes(type, true);
}
/// <summary>
/// Determines whether the member is decorated with one or more attributes of the specified type.
/// </summary>
/// <typeparam name="T">The type of attribute to search for.</typeparam>
/// <param name="member">The member to examine.</param>
/// <returns><see langword="True"/> if the member is decorated with one or more attributes of the type, otherwise <see langword="false"/>.</returns>
public static bool HasAttribute<T>(this ICustomAttributeProvider member)
where T : Attribute
{
return member.IsDefined(typeof(T), true);
}
/// <summary>
/// Determines whether the member is decorated with one or more attributes of the specified type.
/// </summary>
/// <param name="member">The member to examine.</param>
/// <param name="type">The type of attribute to search for.</param>
/// <returns><see langword="True"/> if the member is decorated with one or more attributes of the type, otherwise <see langword="false"/>.</returns>
public static bool HasAttribute(this ICustomAttributeProvider member, Type type)
{
return member.IsDefined(type, true);
}
/// <summary>
/// Determines whether the member is decorated with an attribute that matches the one provided.
/// </summary>
/// <typeparam name="T">The type of attribute to search for.</typeparam>
/// <param name="member">The member to examine.</param>
/// <param name="attributeToMatch">The attribute to match against.</param>
/// <returns><see langword="True"/> if the member is decorated with a matching attribute, otherwise <see langword="false"/>.</returns>
public static bool HasMatchingAttribute<T>(this ICustomAttributeProvider member, T attributeToMatch)
where T : Attribute
{
T[] attributes = member.GetAllAttributes<T>();
if ((attributes == null) ||
(attributes.Length == 0))
{
return false;
}
return attributes.Any(attribute => attribute.Match(attributeToMatch));
}
}
}
AlternateInterceptorRegistrationStrategy.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Ninject;
using Ninject.Components;
using Ninject.Extensions.Interception;
using Ninject.Extensions.Interception.Advice;
using Ninject.Extensions.Interception.Planning.Directives;
using Ninject.Extensions.Interception.Registry;
using Ninject.Planning;
using Ninject.Planning.Strategies;
namespace Common.Interception.Interceptors
{
/// <summary>
/// This is a derivation of InterceptorRegistrationStrategy from Ninject.Extensions.Interception.Planning.Strategies, merged with
/// http://stackoverflow.com/questions/6386461/ninject-intercept-any-method-with-certain-attribute
/// </summary>
public class AlternateInterceptorRegistrationStrategy<TAttribute, TInterceptor> : NinjectComponent, IPlanningStrategy
where TAttribute : Attribute
where TInterceptor : IInterceptor
{
protected const BindingFlags DefaultBindingFlags =
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance;
public AlternateInterceptorRegistrationStrategy(IAdviceFactory adviceFactory, IAdviceRegistry adviceRegistry, IKernel kernel)
{
AdviceFactory = adviceFactory;
AdviceRegistry = adviceRegistry;
Kernel = kernel;
}
public IKernel Kernel { get; set; }
public IAdviceFactory AdviceFactory { get; set; }
public IAdviceRegistry AdviceRegistry { get; set; }
public virtual void Execute(IPlan plan)
{
IEnumerable<MethodInfo> candidates = GetCandidateMethods(plan.Type);
RegisterClassInterceptors(plan.Type, plan, candidates);
foreach (MethodInfo method in candidates)
{
PropertyInfo property = method.GetPropertyFromMethod(plan.Type);
ICustomAttributeProvider provider = (ICustomAttributeProvider)property ?? method;
TAttribute[] attributes = provider.GetAllAttributes<TAttribute>();
if (attributes.Length == 0)
{
continue;
}
RegisterMethodInterceptor(plan.Type, method);
// Indicate that instances of the type should be proxied.
if (!plan.Has<ProxyDirective>())
{
plan.Add(new ProxyDirective());
}
}
}
protected virtual void RegisterClassInterceptors(Type type, IPlan plan, IEnumerable<MethodInfo> candidates)
{
var attributes = type.GetAllAttributes<TAttribute>();
if (attributes.Length == 0)
{
return;
}
foreach (MethodInfo method in candidates)
{
PropertyInfo property = method.GetPropertyFromMethod(type);
ICustomAttributeProvider provider = (ICustomAttributeProvider) property ?? method;
var config = Kernel.Get<IInterceptorConfig>();
if (config.DoNotInterceptAttribute == null)
{
// A "do not intercept" attribute wasn't defined in the config, so go ahead and register
RegisterMethodInterceptor(type, method);
}
else if (!provider.IsDefined(config.DoNotInterceptAttribute, true))
{
// The method wasn't decorated with the "do not intercept" attribute, so go ahead and register
RegisterMethodInterceptor(type, method);
}
}
if (!plan.Has<ProxyDirective>())
{
plan.Add(new ProxyDirective());
}
}
protected virtual void RegisterMethodInterceptor(Type type, MethodInfo method)
{
IAdvice advice = AdviceFactory.Create(method);
advice.Callback = request => request.Context.Kernel.Get<TInterceptor>();
var config = Kernel.TryGet<IInterceptorConfig>();
if (config != null)
{
advice.Order = config.GetOrder<TInterceptor>();
}
AdviceRegistry.Register(advice);
}
protected virtual IEnumerable<MethodInfo> GetCandidateMethods(Type type)
{
MethodInfo[] methods = type.GetMethods(DefaultBindingFlags);
return methods.Where(ShouldIntercept);
}
protected virtual bool ShouldIntercept(MethodInfo methodInfo)
{
return methodInfo.DeclaringType != typeof(object) &&
!methodInfo.IsPrivate;// &&
//!methodInfo.IsFinal;
}
}
}
IInterceptorConfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Ninject.Extensions.Interception;
namespace Common.Interception.Interceptors
{
public interface IInterceptorConfig
{
IInterceptorConfig SpecifyOrder<TInterceptor>(int order) where TInterceptor : IInterceptor;
IInterceptorConfig SpecifyDoNotInterceptAttribute<TAttribute>() where TAttribute : Attribute;
int GetOrder<TInterceptor>() where TInterceptor : IInterceptor;
Type DoNotInterceptAttribute { get; }
}
}
InterceptorConfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Ninject.Extensions.Interception;
namespace Common.Interception.Interceptors
{
public class InterceptorConfig : IInterceptorConfig
{
private readonly Dictionary<Type, int> _orderDictionary = new Dictionary<Type, int>();
public IInterceptorConfig SpecifyOrder<TInterceptor>(int order) where TInterceptor : IInterceptor
{
_orderDictionary.Add(typeof(TInterceptor), order);
return this;
}
public IInterceptorConfig SpecifyDoNotInterceptAttribute<TAttribute>() where TAttribute : Attribute
{
DoNotInterceptAttribute = typeof(TAttribute);
return this;
}
public int GetOrder<TInterceptor>() where TInterceptor : IInterceptor
{
return _orderDictionary[typeof(TInterceptor)];
}
public Type DoNotInterceptAttribute { get; private set; }
}
}
TraceInterceptor.cs - just a sample interceptor
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Ninject.Extensions.Interception;
namespace Common.Interception.Interceptors
{
public class TraceInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine("Enter Method");
invocation.Proceed();
Console.WriteLine("Exit Method");
}
}
}
Notes: Here is a simple console app that shows how to wire up the attributes/interceptors. This has dependencies on both Ninject.Extensions.Interception.DynamicProxy and Ninject.Extensions.Conventions (and all their required dependencies)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Common.Interception.Attributes;
using Common.Interception.Interceptors;
using Ninject;
using Ninject.Extensions.Conventions;
using Ninject.Planning.Strategies;
using SomeProject.Infrastructure;
namespace SomeProject.ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var kernel = new StandardKernel();
kernel.Components.Add<IPlanningStrategy, AlternateInterceptorRegistrationStrategy<TraceAttribute, TraceInterceptor>>();
kernel.Components.Add<IPlanningStrategy, AlternateInterceptorRegistrationStrategy<AuthorizeAttribute, AuthorizeInterceptor>>();
// I needed a way to specify execution order and "DoNotIntercept" without deriving from attributes that would force ninject references all over my domain
// not 100% confident this is the best way - but it works
kernel.Bind<IInterceptorConfig>().ToConstant(new InterceptorConfig()
.SpecifyOrder<TraceInterceptor>(1)
.SpecifyOrder<AuthorizeInterceptor>(0)
.SpecifyDoNotInterceptAttribute<DoNotInterceptAttribute>());
// SomeProject.Infrastructure contains my service classes decorated with my custom attributes
kernel.Bind(x => x.FromAssembliesMatching("SomeProject.Infrastructure")
.SelectAllClasses()
.BindToSelf());
var a = kernel.Get<SomeServiceA>();
var b = kernel.Get<SomeServiceB>();
Console.WriteLine("Calling a.DoSomeStuff()...");
a.DoSomeStuff();
Console.WriteLine("Calling b.DoMyThing()...");
b.DoMyThing();
Console.WriteLine("Calling b.NowTraceThis()...");
b.NowTraceThis();
Console.ReadLine();
}
}
}
After scouring and scouring, I found that a similar question had been asked and answered here: Ninject Intercept any method with certain attribute?
I used this in conjunction with the source code of the InterceptorRegistrationStrategy class within Ninject.Extensions.Interception.Planning.Strategies to create a derivation which I am now using.
crickets
I'm currently experiencing some issues with workflow services.
They work fine if I start 4, 5 in short sequence, but if I increase this value (starting from ~10) then I get the following exception:
This channel can no longer be used to send messages as the output session was auto-closed due to a server-initiated shutdown. Either disable auto-close by setting the DispatchRuntime.AutomaticInputSessionShutdown to false, or consider modifying the shutdown protocol with the remote server.
I think that the problem is in the way I create proxies. I use the following code to provide proxies, attempting to reuse existing ones:
public abstract class ProxyProvider<TService>
where TService : class
{
/// <summary>
/// Static reference to the current time provider.
/// </summary>
private static ProxyProvider<TService> current = DefaultProxyProvider.Instance;
private TService service;
/// <summary>
/// Gets or sets the current time provider.
/// </summary>
/// <value>
/// The current time provider.
/// </value>
public static ProxyProvider<TService> Current
{
get
{
return ProxyProvider<TService>.current;
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
ProxyProvider<TService>.current = value;
}
}
/// <summary>
/// Resets to default.
/// </summary>
public static void ResetToDefault()
{
ProxyProvider<TService>.current = DefaultProxyProvider.Instance;
}
/// <summary>
/// Loads the proxy.
/// </summary>
/// <param name="forceNew">if set to <c>true</c> [force new].</param>
/// <returns>The instance of the proxy.</returns>
public virtual TService Provide(bool forceNew = false)
{
if (forceNew || !this.IsInstanceValid())
{
this.service = this.CreateInstance();
return this.service;
}
return this.service;
}
/// <summary>
/// Internals the load.
/// </summary>
/// <returns>The new created service.</returns>
protected abstract TService CreateInstance();
private bool IsInstanceValid()
{
var instance = this.service as ICommunicationObject;
if (instance == null)
{
return false;
}
return instance.State != CommunicationState.Faulted && instance.State != CommunicationState.Closed && instance.State != CommunicationState.Closing;
}
/// <summary>
/// Defines the default <see cref="ProxyProvider<TService>"/> which uses the System DateTime.UtcNow value.
/// </summary>
private sealed class DefaultProxyProvider : ProxyProvider<TService>
{
/// <summary>
/// Reference to the instance of the <see cref="ProxyProvider<TService>"/>.
/// </summary>
private static ProxyProvider<TService> instance;
/// <summary>
/// Gets the instance.
/// </summary>
public static ProxyProvider<TService> Instance
{
get
{
if (DefaultProxyProvider.instance == null)
{
DefaultProxyProvider.instance = new DefaultProxyProvider();
}
return DefaultProxyProvider.instance;
}
}
/// <summary>
/// Loads the specified force new.
/// </summary>
/// <returns>A non-disposed instance of the given service.</returns>
protected override TService CreateInstance()
{
var loadedService = Activator.CreateInstance<TService>();
return loadedService;
}
}
With an additional "lazy" provider:
public class CustomConstructorProxyProvider<TService> : ProxyProvider<TService>
where TService : class
{
private readonly Func<TService> constructor;
/// <summary>
/// Initializes a new instance of the <see cref="CustomConstructorProxyProvider<TService>"/> class.
/// </summary>
/// <param name="constructor">The constructor.</param>
public CustomConstructorProxyProvider(Func<TService> constructor)
{
this.constructor = constructor;
}
/// <summary>
/// Internals the load.
/// </summary>
/// <returns>The new created service.</returns>
protected override TService CreateInstance()
{
var service = this.constructor();
return service;
}
}
Used this way:
var proxy = ProxyProvider<IWorkflowService>.Current.Provide();
proxy.DoSomething();
Initialized like this:
ProxyProvider<IWorkflowService>.Current = new CustomConstructorProxyProvider<IWorkflowService>(() => new WorkflowServiceProxy("endpoint"));
Workflow services are hosted by IIS and I added the following throttling settings:
<serviceThrottling
maxConcurrentCalls="512"
maxConcurrentInstances="2147483647"
maxConcurrentSessions="1024"/>
which should be enough for my needs.
I hope that someone can help me configuring client and server to have achieve the desired scalability (a few hundreds started in sequence and running in parallel, using the WorkflowInstance sql store).
UPDATE:
I'm using NetTcpBinding for all services.
UPDATE 2:
All services are hosted and consumed by now locally.
Thanks
Francesco
I have created a service data access layer where there are multiple databases where data needs to come from.
I was doing fine with one database where I defined the memberRepository that contained member details. However, now I have to get session-related details that are stored in another database.
OprationContracts:
IMemberServices contains GetLoggedInBuddies(int profileID);
ISessionServices contains GetProfileIDFromSessionID(string sessionID);
My service class:
public class MemberService : IMemberService, ISessionServices
{
#region Strategy pattern configuration
//
// Member repo
//
private MemberRepository memberRepository;
public MemberService()
: this(new MemberRepository())
{ }
public MemberService(MemberRepository memberRepository)
{
this.memberRepository = memberRepository;
}
//
// Session repo
//
private SessionRepository sessionRepository;
public MemberService() : this(new SessionRepository()){}
public MemberService(SessionRepository sessionRepository)
{
this.sessionRepository = sessionRepository;
}
#endregion
/// <summary>
/// Session-related details are maintained in the Secondary database
/// </summary>
/// <param name="sessionID"></param>
/// <returns></returns>
public int GetProfileIDFromSessionID(string sessionID)
{
int sessionProfileID = sessionRepository.GetProfileDetailsFromSessionID(sessionRepository);
return sessionProfileID;
}
/// <summary>
/// Try profileID = 1150526
/// </summary>
/// <param name="profileID"></param>
public void GetLoggedInBuddies(int profileID)
{
memberRepository.GetLoggedInBuddies(profileID);
//return memberRepository.GetLoggedInBuddies(profileID);
}
The issue is that in the // Session Repo section, as I already have a constructor defined. I get that.
So basically in each method I want to do something like
MemberService useSessionRepo = new MemberService(SessionRepository);
useSessionRepo.GetProfileDetailsFromSessionID(...);
MemberService useMemberRepo = new MemberService(MemberRepository);
useMemberRepo.GetLoggedInBuddies(...);
Just need a hand putting this together.
Thanks.
I'm not sure about your issue, but you can use a ctor without param, and with param for each repo.
public MemberService()
{
this.memberRepository = new MemberRepository();
this.sessionRepository = new SessionRepository();
}
I created a central repository that accepts the name of the connection string of the database I want to connect to.
public abstract class DatabaseRepository : BaseRepository
{
static IDbConnection connection;
/// <summary>
/// Handles db connectivity as Dapper assumes an existing connection for all functions
/// Since this app uses three databases, pass in the connection string for the required db.
/// </summary>
/// <returns></returns>
protected static IDbConnection OpenConnection(string connectionStringName)
{
try
{
connection = new SqlConnection(WebConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString);
//connection = SqlMapperUtil.GetOpenConnection(connectionStringName); // if we want to use the Dapper utility methods
connection.Open();
return connection;
}
catch (Exception ex)
{
ErrorLogging.Instance.Fatal(ex); // uses singleton for logging
return null;
}
}
.
.
.
Then in my service library, I make the connection to the appropriate db and perform whatever queries I need:
using (IDbConnection connection = OpenConnection("FirstDBConnectionString")) { ...
Imagine I have the following classes and interfaces:
public interface IService<T> { }
public class DefaultService<T> : IService<T> { }
public class FooService : IService<Foo> { }
public class BarService : IService<Bar> { }
I would then like to be able to get instances from the Kernel like this:
Kernel.Get<IService<Foo>>(); // Should return FooService
Kernel.Get<IService<Bar>>(); // Should return BarService
Kernel.Get<IService<Dog>>(); // Should return DefaultService
Kernel.Get<IService<Cat>>(); // Should return DefaultService
Kernel.Get<IService<Giraffe>>(); // Should return DefaultService
Is it possible to setup bindings using NInject (possibly using the Conventions extension), so that I don't have to manually bind every single possible implementation of IService?
I've been working on something similar recently and came up with somewhat simpler solution of your problem (although a bit weaker).
What should suffice is to bind a generic implementation (DefaultService) to the generic interface, and concrete implementations (FooService, BarService) to the concrete interfaces. When you ask for a concrete instance of the interface, Ninject resolves whether you defined the concrete binding. If you did, it gives you the appropriate instance, otherwise it falls through to the generic binding. The following code should do the trick.
var kernel = new StandardKernel();
kernel.Bind(typeof(IService<>)).To(typeof(DefaultService<>));
kernel.Bind<IService<Foo>>().To<FooService>();
kernel.Bind<IService<Bar>>().To<BarService>();
EDIT:
The concept works throughout the whole Ninject, so you can use it along with Extensions.Conventions as well.
e.g. define the following:
public class Foo{}
public class Bar{}
public class Dog{}
public interface IService<T>{}
public class DefaultService<T> : IService<T>{}
public class FooService : IService<Foo>{}
public class BarService : IService<Bar>{}
use conventions to bind the services:
kernel.Bind(x => x.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom(typeof(IService<>))
.BindSingleInterface());
and create and check the appropriate services:
Assert.IsInstanceOf<BarService>(kernel.Get<IService<Bar>>());
Assert.IsInstanceOf<FooService>(kernel.Get<IService<Foo>>());
Assert.IsInstanceOf<DefaultService<Dog>>(kernel.Get<IService<Dog>>());
I took the liberty of refactoring the answer from #cbp, so that it works for the new IBindingGenerator signature in Ninject v3 conventions. It's pretty much replacing the Process() method signature with the CreateBindings() method signature, but I didn't test this, so there's a chance you'll have to tweak it a bit if you use it.
/// <summary>
/// Creates bindings on open generic types.
/// This is similar to the out-of-the-box
/// <see cref="GenericBindingGenerator" />,
/// but allows a default class to be
/// specified if no other bindings can be found.
/// See the test case for usages.
/// </summary>
public class GenericBindingGeneratorWithDefault : IBindingGenerator
{
private static readonly Type TypeOfObject = typeof(object);
private readonly Type _contractType;
private readonly Dictionary<Type, Type> _cachedBindings;
private readonly Type _defaultType;
public GenericBindingGeneratorWithDefault(Type contractType, Type defaultType)
{
if (!(contractType.IsGenericType || contractType.ContainsGenericParameters))
throw new ArgumentException("The contract must be an open generic type.",
"contractType");
_cachedBindings = new Dictionary<Type, Type>();
_contractType = contractType;
_defaultType = defaultType;
}
/// <summary>
/// Creates the bindings for a type.
/// </summary>
/// <param name="type">The type for which the bindings are created.</param>
/// <param name="bindingRoot">The binding root that is used to create the bindings.</param>
/// <returns>
/// The syntaxes for the created bindings to configure more options.
/// </returns>
public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
{
if (type == null) throw new ArgumentNullException("type");
if (bindingRoot == null) throw new ArgumentNullException("bindingRoot");
if (type.IsInterface || type.IsAbstract) yield break;
if (type == _defaultType)
{
yield return bindingRoot.Bind(_contractType).ToMethod(
ctx =>
{
Type requestedType = ctx.Request.Service;
Type resolution = _cachedBindings.ContainsKey(requestedType)
? _cachedBindings[requestedType]
: _defaultType.MakeGenericType(ctx.GenericArguments);
return ctx.Kernel.Get(resolution);
});
}
else
{
Type interfaceType = ResolveClosingInterface(type);
if (interfaceType != null)
{
yield return bindingRoot.Bind(type).To(_cachedBindings[interfaceType]);
}
}
}
/// <summary>
/// Resolves the closing interface.
/// </summary>
/// <param name="targetType">Type of the target.</param>
/// <returns></returns>
private Type ResolveClosingInterface(Type targetType)
{
if (targetType.IsInterface || targetType.IsAbstract) return null;
do
{
Type[] interfaces = targetType.GetInterfaces();
foreach (Type #interface in interfaces)
{
if (!#interface.IsGenericType) continue;
if (#interface.GetGenericTypeDefinition() == _contractType)
{
return #interface;
}
}
targetType = targetType.BaseType;
} while (targetType != TypeOfObject);
return null;
}
}
I figured out how to do this after a couple of hours messing around with NInject Convention's GenericBindingGenerator.
If anyone is interested I can post it.
Update:
/// <summary>
/// Creates bindings on open generic types.
/// This is similar to the out-of-the-box <see cref="GenericBindingGenerator" />, but allows a default class to be
/// specified if no other bindings can be found. See the test case for usages.
/// </summary>
public class GenericBindingGeneratorWithDefault : IBindingGenerator
{
private static readonly Type TYPE_OF_OBJECT = typeof (object);
private readonly Type _contractType;
private Dictionary<Type, Type> _cachedBindings = new Dictionary<Type, Type>();
private readonly Type _defaultType;
public GenericBindingGeneratorWithDefault(Type contractType, Type defaultType)
{
if ( !( contractType.IsGenericType || contractType.ContainsGenericParameters ) )
{
throw new ArgumentException( "The contract must be an open generic type.", "contractType" );
}
_contractType = contractType;
_defaultType = defaultType;
}
/// <summary>
/// Processes the specified type creating kernel bindings.
/// </summary>
/// <param name="type">The type to process.</param>
/// <param name="scopeCallback">the scope callback.</param>
/// <param name="kernel">The kernel to configure.</param>
public void Process( Type type, Func<IContext, object> scopeCallback, IKernel kernel )
{
if (type == _defaultType)
{
kernel.Bind(_contractType).ToMethod(
ctx =>
{
var requestedType = ctx.Request.Service;
var resolution = _cachedBindings.ContainsKey(requestedType)
? _cachedBindings[requestedType]
: _defaultType.MakeGenericType(ctx.GenericArguments);
return ctx.Kernel.Get(resolution);
});
}
else
{
Type interfaceType = ResolveClosingInterface(type);
if (interfaceType != null)
{
_cachedBindings[interfaceType] = type;
}
}
}
/// <summary>
/// Resolves the closing interface.
/// </summary>
/// <param name="targetType">Type of the target.</param>
/// <returns></returns>
public Type ResolveClosingInterface( Type targetType )
{
if ( targetType.IsInterface || targetType.IsAbstract )
{
return null;
}
do
{
Type[] interfaces = targetType.GetInterfaces();
foreach ( Type #interface in interfaces )
{
if ( !#interface.IsGenericType )
{
continue;
}
if ( #interface.GetGenericTypeDefinition() == _contractType )
{
return #interface;
}
}
targetType = targetType.BaseType;
} while ( targetType != TYPE_OF_OBJECT );
return null;
}
}
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 2 years ago.
Improve this question
can anyone provide/refer a proper OO type helper class for managing a singleton of the SessionFactory and then also for managing Sessions?
Check out Billy McCafferty's work. His earlier version had some limitations, you'll need to correct the error handling around closing and flushing, and I'm not sure i have it right but I will post how I modified his stuff.
Billy is also working on a new framework that uses MVC & nHibernate called S#arp Architechure which I'm currently using, and so far so good.
Anyways here's my modified version of his code. I make no guanrtees on accuracy or completness and you are using this on your own risk. If you use this make sure you close the session out. If you have any questions drop me an email [joshua][dot][berke] at [gmail...you know the rest].
/// <summary>
/// Handles creation and management of sessions and transactions. It is a singleton because
/// building the initial session factory is very expensive. Inspiration for this class came
/// from Chapter 8 of Hibernate in Action by Bauer and King. Although it is a sealed singleton
/// you can use TypeMock (http://www.typemock.com) for more flexible testing.
/// </summary>
public sealed class NHibernateSessionManager
{
private const string DefaultConfigFile = "DefaultAppWeb.Config";
private static readonly object _syncRoot = new object();
#region Thread-safe, lazy Singleton
/// <summary>
/// This is a thread-safe, lazy singleton. See http://www.yoda.arachsys.com/csharp/singleton.html
/// for more details about its implementation.
/// </summary>
public static NHibernateSessionManager Instance
{
get
{
return Nested.NHibernateSessionManager;
}
}
/// <summary>
/// Private constructor to enforce singleton
/// </summary>
private NHibernateSessionManager() { }
/// <summary>
/// Assists with ensuring thread-safe, lazy singleton
/// </summary>
private class Nested
{
static Nested() { }
internal static readonly NHibernateSessionManager NHibernateSessionManager =
new NHibernateSessionManager();
}
#endregion
/// <summary>
/// This method attempts to find a session factory stored in <see cref="sessionFactories" />
/// via its name; if it can't be found it creates a new one and adds it the hashtable.
/// </summary>
/// <param name="sessionFactoryConfigPath">Path location of the factory config</param>
private ISessionFactory GetSessionFactoryFor(string sessionFactoryConfigPath)
{
Check.Require(!string.IsNullOrEmpty(sessionFactoryConfigPath),
"sessionFactoryConfigPath may not be null nor empty");
// Attempt to retrieve a stored SessionFactory from the hashtable.
ISessionFactory sessionFactory;// = (ISessionFactory)sessionFactories[sessionFactoryConfigPath];
// try and get a session factory if we don't find one create it
lock (_syncRoot)
{
if (!sessionFactories.TryGetValue(sessionFactoryConfigPath, out sessionFactory))
{
Configuration cfg = new Configuration();
if (sessionFactoryConfigPath != DefaultConfigFile)
{
Check.Require(File.Exists(sessionFactoryConfigPath),
"The config file at '" + sessionFactoryConfigPath + "' could not be found");
cfg.Configure(sessionFactoryConfigPath);
}
else
{
cfg.Configure();
}
// Now that we have our Configuration object, create a new SessionFactory
sessionFactory = cfg.BuildSessionFactory();
Check.Ensure(sessionFactory != null, "sessionFactory is null and was not built");
sessionFactories.Add(sessionFactoryConfigPath, sessionFactory);
}
}
return sessionFactory;
}
/// <summary>
/// Allows you to register an interceptor on a new session. This may not be called if there is already
/// an open session attached to the HttpContext. If you have an interceptor to be used, modify
/// the HttpModule to call this before calling BeginTransaction().
/// </summary>
public void RegisterInterceptorOn(string sessionFactoryConfigPath, IInterceptor interceptor)
{
ISession session = (ISession)ContextSessions[sessionFactoryConfigPath];
if (session != null && session.IsOpen)
{
throw new CacheException("You cannot register an interceptor once a session has already been opened");
}
GetSessionFrom(sessionFactoryConfigPath, interceptor);
}
public ISession GetSessionFrom(string sessionFactoryConfigPath)
{
return GetSessionFrom(sessionFactoryConfigPath, null);
}
/// <summary>
/// Gets or creates an ISession using the web / app config file.
/// </summary>
/// <returns></returns>
public ISession GetSessionFrom()
{
return GetSessionFrom(DefaultConfigFile, null);
}
/// <summary>
/// Gets a session with or without an interceptor. This method is not called directly; instead,
/// it gets invoked from other public methods.
/// </summary>
private ISession GetSessionFrom(string sessionFactoryConfigPath, IInterceptor interceptor)
{
var allSessions = ContextSessions;
ISession session = null;
if (!allSessions.TryGetValue(sessionFactoryConfigPath, out session))
{
if (interceptor != null)
{
session = GetSessionFactoryFor(sessionFactoryConfigPath).OpenSession(interceptor);
}
else
{
session = GetSessionFactoryFor(sessionFactoryConfigPath).OpenSession();
}
allSessions[sessionFactoryConfigPath] = session;
}
//session.FlushMode = FlushMode.Always;
Check.Ensure(session != null, "session was null");
return session;
}
/// <summary>
/// Flushes anything left in the session and closes the connection.
/// </summary>
public void CloseSessionOn(string sessionFactoryConfigPath)
{
ISession session;
if (ContextSessions.TryGetValue(sessionFactoryConfigPath, out session))
{
if (session.IsOpen)
{
session.Flush();
session.Close();
}
ContextSessions.Remove(sessionFactoryConfigPath);
}
}
public ITransaction BeginTransactionOn(string sessionFactoryConfigPath)
{
ITransaction transaction;
if (!ContextTransactions.TryGetValue(sessionFactoryConfigPath, out transaction))
{
transaction = GetSessionFrom(sessionFactoryConfigPath).BeginTransaction();
ContextTransactions.Add(sessionFactoryConfigPath, transaction);
}
return transaction;
}
public void CommitTransactionOn(string sessionFactoryConfigPath)
{
try
{
if (HasOpenTransactionOn(sessionFactoryConfigPath))
{
ITransaction transaction = (ITransaction)ContextTransactions[sessionFactoryConfigPath];
transaction.Commit();
ContextTransactions.Remove(sessionFactoryConfigPath);
}
}
catch (HibernateException he)
{
try
{
RollbackTransactionOn(sessionFactoryConfigPath);
}
finally
{
throw he;
}
}
}
public bool HasOpenTransactionOn(string sessionFactoryConfigPath)
{
ITransaction transaction;
if (ContextTransactions.TryGetValue(sessionFactoryConfigPath, out transaction))
{
return !transaction.WasCommitted && !transaction.WasRolledBack;
}
return false;
}
public void RollbackTransactionOn(string sessionFactoryConfigPath)
{
try
{
if (HasOpenTransactionOn(sessionFactoryConfigPath))
{
ITransaction transaction = (ITransaction)ContextTransactions[sessionFactoryConfigPath];
transaction.Rollback();
}
ContextTransactions.Remove(sessionFactoryConfigPath);
}
finally
{
ForceCloseSessionOn(sessionFactoryConfigPath);
}
}
/// <summary>
/// Since multiple databases may be in use, there may be one transaction per database
/// persisted at any one time. The easiest way to store them is via a hashtable
/// with the key being tied to session factory. If within a web context, this uses
/// <see cref="HttpContext" /> instead of the WinForms specific <see cref="CallContext" />.
/// Discussion concerning this found at http://forum.springframework.net/showthread.php?t=572
/// </summary>
private Dictionary<string, ITransaction> ContextTransactions
{
get
{
if (IsInWebContext())
{
if (HttpContext.Current.Items[TRANSACTION_KEY] == null)
HttpContext.Current.Items[TRANSACTION_KEY] = new Dictionary<string, ITransaction>();
return (Dictionary<string, ITransaction>)HttpContext.Current.Items[TRANSACTION_KEY];
}
else
{
if (CallContext.GetData(TRANSACTION_KEY) == null)
CallContext.SetData(TRANSACTION_KEY, new Dictionary<string, ITransaction>());
return (Dictionary<string, ITransaction>)CallContext.GetData(TRANSACTION_KEY);
}
}
}
/// <summary>
/// Since multiple databases may be in use, there may be one session per database
/// persisted at any one time. The easiest way to store them is via a hashtable
/// with the key being tied to session factory. If within a web context, this uses
/// <see cref="HttpContext" /> instead of the WinForms specific <see cref="CallContext" />.
/// Discussion concerning this found at http://forum.springframework.net/showthread.php?t=572
/// </summary>
private Dictionary<string, ISession> ContextSessions
{
get
{
if (IsInWebContext())
{
if (HttpContext.Current.Items[SESSION_KEY] == null)
HttpContext.Current.Items[SESSION_KEY] = new Dictionary<string, ISession>();
return (Dictionary<string, ISession>)HttpContext.Current.Items[SESSION_KEY];
}
else
{
if (CallContext.GetData(SESSION_KEY) == null)
CallContext.SetData(SESSION_KEY, new Dictionary<string, ISession>());
return (Dictionary<string, ISession>)CallContext.GetData(SESSION_KEY);
}
}
}
private bool IsInWebContext()
{
return HttpContext.Current != null;
}
private Dictionary<string, ISessionFactory> sessionFactories = new Dictionary<string, ISessionFactory>();
private const string TRANSACTION_KEY = "CONTEXT_TRANSACTIONS";
private const string SESSION_KEY = "CONTEXT_SESSIONS";
public bool HasOpenTransactionOn()
{
return HasOpenTransactionOn(DefaultConfigFile);
}
public void CommitTransactionOn()
{
CommitTransactionOn(DefaultConfigFile);
}
public void CloseSessionOn()
{
CloseSessionOn(DefaultConfigFile);
}
public void ForceCloseSessionOn()
{
ForceCloseSessionOn(DefaultConfigFile);
}
public void ForceCloseSessionOn(string sessionFactoryConfigPath)
{
ISession session;
if (ContextSessions.TryGetValue(sessionFactoryConfigPath, out session))
{
if (session.IsOpen)
{
session.Close();
}
ContextSessions.Remove(sessionFactoryConfigPath);
}
}
public void BeginTransactionOn()
{
this.BeginTransactionOn(DefaultConfigFile);
}
public void RollbackTransactionOn()
{
this.RollbackTransactionOn(DefaultConfigFile);
}
}
I've had great success in the past using Spring.NET's NHibernate support modules. See http://www.springframework.net/downloads/Spring.Data.NHibernate/. You should be able to use the OpenSessionInView module and extend your DAOs off of the NHibernateSupport DAO to get full management support of the open session in view pattern.
Additionally, although I've never tried it, you should be able to use the above stated framework even if you opt out of the reset of Spring.NET's offerings (namely IoC and AOP).
Sure, this is what I used when I was getting started with NHibernate:
Session Factory
public class BaseDataAccess
{
protected ISession m_session;
public BaseDataAccess()
{
m_session = NHibernateHttpModule.CurrentSession;
}
public static ISession OpenSession()
{
Configuration config;
ISessionFactory factory;
ISession session;
config = new Configuration();
if (config == null)
{
throw new ArgumentNullException(nameof(config));
}
if (factory == null)
{
throw new ArgumentNullException(nameof(factory);
}
if (session == null)
{
throw new ArgumentNullException(nameof(session));
}
config.AddAssembly("My.Assembly.Here");
factory = config.BuildSessionFactory();
session = factory.OpenSession();
return session;
}
}
Let me know if that helps.
Two suggestions:
Jeffrey Palermo's HybridSessionBuilder (jeffreypalermo.com/blog/use-this-nhibernate-wrapper-to-keep-your-repository-classes-simple)
See the code examples (specifically see Session 13) in the Summer of NHibernate (www.summerofnhibernate.com)
You might like to consider making your DAL less concerned with managing NHibernate sessions by leveraging NHibernate.Burrow (or implementing a similar pattern yourself).
"NHibernate.Burrow is a light weight middleware developed to support .Net applications using NHibernate by providing advanced and smart session/transaction management and other facilitates."
If you decide to roll your own there are some useful links at the bottom of this page:
A useful google search term would be 'NHibernate Session Management' and 'Contextual Sessions'...
There's no shortage of ideas - you could say there are too many choices, hopefully opinion will start to gravitate around Burrow or something like it...