2014-01-04 4 views
2

Я новичок в Castle, NHibernate и WCF.Wcf NHibernate Управление сеансом

Я реализовал управление сеансами для моего приложения MVC на основе следующей статье, потому что seemd быть самым передовым выполнение всех должностей, которые я читал до сих пор: http://nhibernate.info/blog/2011/03/02/effective-nhibernate-session-management-for-web-apps.html

Единственная проблема, которую я получил, что это использует некоторые специальные функции Asp.net, которые недоступны в моем сервисе WCF, например (HttpContext.Current.Items).

Я начал использовать WcfFacility

public void Install(IWindsorContainer container, IConfigurationStore store) 
    { 
     container.AddFacility<WcfFacility>().Register 
    (

     Component.For<IRepository>().ImplementedBy(typeof(RepositoryBase<,>)), 
     Component.For<ITimeService>() 
        .ImplementedBy<myTimeMvc.Webservice.TimeService>() 
        .Named("myTimeMvc.Webservice.TimeService")); 


     container.Register(
     Component.For<IServiceBehavior>() 
      .ImplementedBy<WcfSessionPerRequestBehavior>() 
     ); 
    } 

Мои Настойчивость конфигурации:

public class PersistenceInstaller : IWindsorInstaller 
{ 
    public void Install(IWindsorContainer container, IConfigurationStore store) 
    { 

     container.Kernel.AddFacility<TypedFactoryFacility>(); 

     container.Register(Component.For<ISessionFactory>().UsingFactoryMethod(k => CreateNhSessionFactory())); 

     container.Register(Component.For<ISessionFactoryProvider>().AsFactory()); 

     container.Register(Component.For<IEnumerable<ISessionFactory>>().UsingFactoryMethod(k => k.ResolveAll<ISessionFactory>())); 

     container.Register(Classes.FromAssembly(Assembly.GetAssembly(typeof(HdtRepository))).InSameNamespaceAs<HdtRepository>().WithService.DefaultInterfaces().LifestyleTransient()); 
    } 

    /// <summary> 
    /// Creates NHibernate Session Factory. 
    /// </summary> 
    /// <returns>NHibernate Session Factory</returns> 
    private static ISessionFactory CreateNhSessionFactory() 
    { 
     var connStr = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString; 
     return Fluently.Configure() 


      .Database(
         MsSqlConfiguration.MsSql2008 
         .UseOuterJoin() 
         .ConnectionString(x => x.FromConnectionStringWithKey("DefaultConnection")) 
         .ShowSql() 
      ) 
      .Mappings(m => m.FluentMappings.AddFromAssemblyOf<TimeRecord>()) 
      .ExposeConfiguration(cfg => 
       cfg.Properties[Environment.CurrentSessionContextClass] = typeof(LazySessionContext).AssemblyQualifiedName 

       ) 


      .BuildSessionFactory(); 
    } 

} 

Тогда я попытался решить проблему с "HttpContext.Current.Items", добавив пользовательское расширение:

namespace MyTimeService.WcfExtension 
{ 
///<summary> 
/// This class incapsulates context information for a service instance 
///</summary> 
public class WcfInstanceContext : IExtension<InstanceContext> 
{ 
    private readonly IDictionary items; 

    private WcfInstanceContext() 
    { 
     items = new Hashtable(); 
    } 

    ///<summary> 
    /// <see cref="IDictionary"/> stored in current instance context. 
    ///</summary> 
    public IDictionary Items 
    { 
     get { return items; } 
    } 

    ///<summary> 
    /// Gets the current instance of <see cref="WcfInstanceContext"/> 
    ///</summary> 
    public static WcfInstanceContext Current 
    { 
     get 
     { 
      WcfInstanceContext context =  OperationContext.Current.InstanceContext.Extensions.Find<WcfInstanceContext>(); 
      if (context == null) 
      { 
       context = new WcfInstanceContext(); 
       OperationContext.Current.InstanceContext.Extensions.Add(context); 
      } 
      return context; 
     } 
    } 

    /// <summary> 
    /// <see cref="IExtension{T}"/> Attach() method 
    /// </summary> 
    public void Attach(InstanceContext owner) { } 

    /// <summary> 
    /// <see cref="IExtension{T}"/> Detach() method 
    /// </summary> 
    public void Detach(InstanceContext owner) { } 
} 
} 

зарегистрирован следующим образом:

<extensions> 
    <behaviorExtensions> 
    <add name="WcfInstanceContext" type="MyTimeService.WcfExtension, MyTimeService.WcfExtension.WcfInstanceContext" /> 
    </behaviorExtensions> 
</extensions> 

Затем я создал обычай ServiceBehavior

public class WcfSessionPerRequestBehavior : IServiceBehavior 
{ 
    private ISessionFactoryProvider _sfp; 


    public WcfSessionPerRequestBehavior(ISessionFactoryProvider sfp) 
    { 
     _sfp = sfp; 
    } 

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) 
    { 
    } 

    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) 
    { 
    } 

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) 
    { 
     foreach (var cdb in serviceHostBase.ChannelDispatchers) 
     { 
      var channelDispatcher = cdb as ChannelDispatcher; 
      if (null != channelDispatcher) 
      { 
       foreach (var endpointDispatcher in channelDispatcher.Endpoints) 
       { 
        foreach (var dispatchOperation in endpointDispatcher.DispatchRuntime.Operations) 
        { 
         dispatchOperation.CallContextInitializers.Add(new WcfSessionPerRequestCallContextInitializer(_sfp)); 
        } 
       } 
      } 
     } 
    } 

следуют пользовательские ICallContextInitializer:

public class WcfSessionPerRequestCallContextInitializer : ICallContextInitializer 
{ 
    private ILogger logger = NullLogger.Instance; 

    public ILogger Logger 
    { 
     get { return logger; } 
     set { logger = value; } 
    } 


    private ISessionFactoryProvider sfp; 


    public WcfSessionPerRequestCallContextInitializer(ISessionFactoryProvider s) 
    { 
     this.sfp = s; 

    } 

    public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message) 
    { 
     foreach (var sf in sfp.GetSessionFactories()) 
     { 
      var localFactory = sf; 
      LazySessionContext.Bind(new Lazy<NHibernate.ISession>(() => BeginSession(localFactory)), sf); 
     } 
     return null; 

    } 

    public void AfterInvoke(object correlationState) 
    { 
     foreach (var sf in sfp.GetSessionFactories()) 
     { 
      var session = LazySessionContext.UnBind(sf); 
      if (session == null) continue; 
      EndSession(session); 
     } 
    } 

    private static NHibernate.ISession BeginSession(ISessionFactory sf) 
    { 
     var session = sf.OpenSession(); 
     session.BeginTransaction(); 
     return session; 
    } 

    private void ContextEndRequest(object sender, EventArgs e) 
    { 
     foreach (var sf in sfp.GetSessionFactories()) 
     { 
      var session = LazySessionContext.UnBind(sf); 
      if (session == null) continue; 
      EndSession(session); 
     } 
    } 

    private static void EndSession(NHibernate.ISession session) 
    { 
     if (session.Transaction != null && session.Transaction.IsActive) 
     { 
      session.Transaction.Commit(); 
     } 
     session.Dispose(); 
    } 
} 

и наконец-то я настроил ICurrentSessionContext:

public class LazySessionContext : ICurrentSessionContext 
{ 
    private readonly ISessionFactoryImplementor factory; 
    private const string CurrentSessionContextKey = "NHibernateCurrentSession"; 

    public LazySessionContext(ISessionFactoryImplementor factory) 
    { 
     this.factory = factory; 
    } 

    /// <summary> 
    /// Retrieve the current session for the session factory. 
    /// </summary> 
    /// <returns></returns> 
    public NHibernate.ISession CurrentSession() 
    { 
     Lazy<NHibernate.ISession> initializer; 
     var currentSessionFactoryMap = GetCurrentFactoryMap(); 
     if (currentSessionFactoryMap == null || !currentSessionFactoryMap.TryGetValue(factory, out initializer)) 
     { 
      return null; 
     } 
     return initializer.Value; 
    } 

    /// <summary> 
    /// Bind a new sessionInitializer to the context of the sessionFactory. 
    /// </summary> 
    /// <param name="sessionInitializer"></param> 
    /// <param name="sessionFactory"></param> 
    public static void Bind(Lazy<NHibernate.ISession> sessionInitializer, ISessionFactory sessionFactory) 
    { 
     var map = GetCurrentFactoryMap(); 
     map[sessionFactory] = sessionInitializer; 
    } 

    /// <summary> 
    /// Unbind the current session of the session factory. 
    /// </summary> 
    /// <param name="sessionFactory"></param> 
    /// <returns></returns> 
    public static NHibernate.ISession UnBind(ISessionFactory sessionFactory) 
    { 
     var map = GetCurrentFactoryMap(); 
     var sessionInitializer = map[sessionFactory]; 
     map[sessionFactory] = null; 
     if (sessionInitializer == null || !sessionInitializer.IsValueCreated) return null; 
     return sessionInitializer.Value; 
    } 

    /// <summary> 
    /// Provides the CurrentMap of SessionFactories. 
    /// If there is no map create/store and return a new one. 
    /// </summary> 
    /// <returns></returns> 
    private static IDictionary<ISessionFactory, Lazy<NHibernate.ISession>> GetCurrentFactoryMap() 
    { 

     //var currentFactoryMap = (IDictionary<ISessionFactory, Lazy<NHibernate.ISession>>)HttpContext.Current.Items[CurrentSessionContextKey]; 


     var currentFactoryMap = (IDictionary<ISessionFactory, Lazy<NHibernate.ISession>>)WcfInstanceContext.Current.Items[CurrentSessionContextKey]; 

     if (currentFactoryMap == null) 
     { 
      currentFactoryMap = new Dictionary<ISessionFactory, Lazy<NHibernate.ISession>>(); 
      WcfInstanceContext.Current.Items[CurrentSessionContextKey] = currentFactoryMap; 
     } 
     return currentFactoryMap; 
    } 
} 

Это кажется но, поскольку я новичок во всем, что сту Если я не сделал это правильно, я не могу сказать. Может ли кто-нибудь взглянуть на него и дать мне отзыв?

Приветствие, Стефан

ответ

1

Вы используете OperationContext.Current, который является правильным способом сделать по требованию реализации контекста для WCF служб, поэтому она выглядит хорошо для меня ...

Вопроса, почему вы не просто используя стандартную реализацию, которая выходит из коробки с nhibernate? Реализация в NHibernate.Context.WcfOperationSessionContext и вы просто должны использовать этот в вашей сессии заводской настройки

Например:

Fluently.Configure() 
    ... 
    .ExposeConfiguration(cfg => cfg.SetProperty(
            Environment.CurrentSessionContextClass, 
            "wcf") 

или Fluently.Configure()...CurrentSessionContext<WcfOperationSessionContext>()

+0

Поскольку я новичок во всем этом, и не знаю, что это лучше: P Когда я реализовал свое приложение MVC, я также подумал, что «PerWebRequest» будет делать все для управления сеансом для меня, но это не было случай. Затем я нашел статью, опубликованную выше, и это сработало как шарм (кроме материалов WCF). Именно по этой причине я попытался сделать мой WCF-сервис таким, как это ... Но я собираюсь расследовать ваше предложение прямо сейчас ... – stefan

1

Вы также можете просто установить aspNetCompatibilityEnabled=true и вы получите HttpContext доступный как для MVC, так и для WCF.

+1

Насколько я знаю, это не рекомендуется. – stefan

Смежные вопросы