2009-09-02 31 views
6

Tôi thực sự mới vào thùng chứa Castle Windsor IoC. Tôi muốn biết nếu theres một cách để lưu trữ các biến phiên sử dụng container IoC. Tôi đã suy nghĩ một cái gì đó trong dòng này:Dự án lâu đài cho mỗi phiên làm việc với ASP.NET MVC

Tôi muốn có một lớp để lưu trữ tùy chọn tìm kiếm:

public interface ISearchOptions{ 
    public string Filter{get;set;} 
    public string SortOrder{get;set;} 
} 

public class SearchOptions{ 
    public string Filter{get;set;} 
    public string SortOrder{get;set;} 
} 

Và sau đó tiêm đó vào lớp mà đã sử dụng nó:

public class SearchController{ 
    private ISearchOptions _searchOptions; 
    public SearchController(ISearchOptions searchOptions){ 
     _searchOptions=searchOptions; 
    } 
    ... 
} 

sau đó trong web.config của tôi, nơi tôi cấu hình lâu đài tôi muốn có một cái gì đó như:

<castle> 
    <components> 
     <component id="searchOptions" service="Web.Models.ISearchOptions, Web" type="Web.Models.SearchOptions, Web" lifestyle="PerSession" /> 
    </components> 
</castle> 

Và có container IoC xử lý đối tượng phiên mà không cần phải tự truy cập một cách rõ ràng.

Tôi làm cách nào để thực hiện việc này?

Cảm ơn.

EDIT: Đã thực hiện một số nghiên cứu. Về cơ bản, những gì tôi muốn là có một phiên Scoped thành phần. Tôi đến từ Java và Spring Framework và ở đó tôi có đậu phiên giao nhau mà tôi nghĩ là rất hữu ích để lưu trữ dữ liệu phiên.

Trả lời

14

điều này có thể là những gì bạn đang tìm kiếm.

public class PerSessionLifestyleManager : AbstractLifestyleManager 
    { 
    private readonly string PerSessionObjectID = "PerSessionLifestyleManager_" + Guid.NewGuid().ToString(); 

    public override object Resolve(CreationContext context) 
    { 
     if (HttpContext.Current.Session[PerSessionObjectID] == null) 
     { 
      // Create the actual object 
      HttpContext.Current.Session[PerSessionObjectID] = base.Resolve(context); 
     } 

     return HttpContext.Current.Session[PerSessionObjectID]; 
    } 

    public override void Dispose() 
    { 
    } 
} 

Và sau đó thêm

<component 
     id="billingManager" 
     lifestyle="custom" 
     customLifestyleType="Namespace.PerSessionLifestyleManager, Namespace" 
     service="IInterface, Namespace" 
     type="Type, Namespace"> 
</component> 
+0

Cảm ơn, đây chính xác là những gì tôi đang tìm kiếm. –

+0

Tôi cũng vậy! Cảm ơn. –

+1

Tôi đoán bạn nên thay đổi tên trường từ PerRequestObjectID thành PerSessionObjectID. Trên thực tế tôi tin rằng việc xem tài liệu có thể giúp bạn có được một chặng đường dài :) http://www.castleproject.org/container/documentation/trunk/usersguide/lifestyles.html – Siewers

1

Có vẻ như bạn đang đi đúng hướng, nhưng lớp SearchOptions của bạn cần phải thực hiện ISearchOptions:

public class SearchOptions : ISearchOptions { ... } 

Bạn cũng cần phải nói Windsor rằng SearchController của bạn là một thành phần, vì vậy bạn có thể muốn đăng ký mà trong web.config là tốt, mặc dù tôi thích làm điều đó từ mã thay vì (xem bên dưới).

Để làm Windsor nhặt web.config của bạn, bạn nên nhanh chóng nó như thế này:

var container = new WindsorContainer(new XmlInterpreter()); 

Để thực hiện một thể hiện mới của SearchController, bạn có thể sau đó chỉ cần làm điều này:

var searchController = container.Resolve<SearchController>(); 

Để đăng ký tất cả các Bộ điều khiển trong một hội đồng nhất định sử dụng các kỹ thuật dựa trên quy ước, bạn có thể làm như sau:

container.Register(AllTypes 
    .FromAssemblyContaining<MyController>() 
    .BasedOn<IController>() 
    .ConfigureFor<IController>(reg => reg.LifeStyle.Transient)); 
4

Giải pháp này sẽ làm việc cho Windsor 3.0 trở lên. Nó dựa trên việc thực hiện Phong cách sống của PerWebRequest và tận dụng phong cách Scoped Lifestyle mới được giới thiệu trong Windsor 3.0.

Bạn cần hai lớp ...

An thực hiện IHttpModule để xử lý quản lý phiên làm việc. Thêm đối tượng ILifetimeScope vào phiên và xử lý nó một lần nữa khi phiên hết hạn. Điều này là rất quan trọng để đảm bảo rằng các thành phần được phát hành đúng cách. Điều này không được quan tâm trong các giải pháp khác được đưa ra ở đây cho đến nay.

public class PerWebSessionLifestyleModule : IHttpModule 
{ 
    private const string key = "castle.per-web-session-lifestyle-cache"; 

    public void Init(HttpApplication context) 
    { 
     var sessionState = ((SessionStateModule)context.Modules["Session"]); 
     sessionState.End += SessionEnd; 
    } 

    private static void SessionEnd(object sender, EventArgs e) 
    { 
     var app = (HttpApplication)sender; 

     var scope = GetScope(app.Context.Session, false); 

     if (scope != null) 
     { 
      scope.Dispose(); 
     } 
    } 

    internal static ILifetimeScope GetScope() 
    { 
     var current = HttpContext.Current; 

     if (current == null) 
     { 
      throw new InvalidOperationException("HttpContext.Current is null. PerWebSessionLifestyle can only be used in ASP.Net"); 
     } 

     return GetScope(current.Session, true); 
    } 

    internal static ILifetimeScope YieldScope() 
    { 
     var context = HttpContext.Current; 

     if (context == null) 
     { 
      return null; 
     } 

     var scope = GetScope(context.Session, true); 

     if (scope != null) 
     { 
      context.Session.Remove(key); 
     } 

     return scope; 
    } 

    private static ILifetimeScope GetScope(HttpSessionState session, bool createIfNotPresent) 
    { 
     var lifetimeScope = (ILifetimeScope)session[key]; 

     if (lifetimeScope == null && createIfNotPresent) 
     { 
      lifetimeScope = new DefaultLifetimeScope(new ScopeCache(), null); 
      session[key] = lifetimeScope; 
      return lifetimeScope; 
     } 

     return lifetimeScope; 
    } 

    public void Dispose() 
    { 
    } 
} 

Lớp thứ hai bạn cần là triển khai IScopeAccessor. Điều này được sử dụng để thu hẹp khoảng cách giữa HttpModule của bạn và được xây dựng trong lớp Windsor ScopedLifestyleManager.

public class WebSessionScopeAccessor : IScopeAccessor 
{ 
    public void Dispose() 
    { 
     var scope = PerWebSessionLifestyleModule.YieldScope(); 
     if (scope != null) 
     { 
      scope.Dispose(); 
     } 
    } 

    public ILifetimeScope GetScope(CreationContext context) 
    { 
     return PerWebSessionLifestyleModule.GetScope(); 
    } 
} 

Hai phương pháp internal static được thêm vào PerWebSessionLifestyleModule để hỗ trợ điều này.

Vậy đó, mong đợi để đăng ký nó ...

container.Register(Component 
    .For<ISometing>() 
    .ImplementedBy<Something>() 
    .LifestyleScoped<WebSessionScopeAccessor>()); 

tùy ý, tôi quấn đăng ký này thành một phương pháp khuyến nông ...

public static class ComponentRegistrationExtensions 
{ 
    public static ComponentRegistration<TService> LifestylePerSession<TService>(this ComponentRegistration<TService> reg) 
     where TService : class 
    { 
     return reg.LifestyleScoped<WebSessionScopeAccessor>(); 
    } 
} 

Vì vậy, nó có thể được gọi như thế này. ..

container.Register(Component 
    .For<ISometing>() 
    .ImplementedBy<Something>() 
    .LifestylePerSession()); 
+1

IIRC Session_End sẽ chỉ kích hoạt các phiên ** InProc **. –

+0

Vì vậy, nó sẽ có vẻ. Tôi đã không thực sự xem xét điều này. Tôi nghĩ là tốt trong tình huống của tôi. Tôi chắc chắn tôi sẽ chỉ sử dụng các phiên InProc. Loại đối tượng mà tôi đang quản lý lối sống sẽ không bao giờ cần phải được duy trì nếu phiên hết hạn. Nhưng đây chắc chắn là điều mà người khác nên biết.Tôi không chắc chắn làm thế nào bạn có thể xử lý lối sống phiên mà không có sự kiện kết thúc phiên. –

+0

thực sự, không có cách nào. Tôi đã viết một lối sống perwebsession cho Windsor một thời gian trước đây, xem http://bugsquash.blogspot.com/2010/06/hybrid-lifestyles-in-windsor.html https://github.com/castleprojectcontrib/Castle.Windsor.Lifestyles –

1

Trải nghiệm của tôi là Andy's answer không hoạt động, như SessionStateModule.Endnever raised directly:

Mặc dù sự kiện kết thúc là công khai, bạn chỉ có thể xử lý sự kiện bằng cách thêm trình xử lý sự kiện vào tệp Global.asax. Hạn chế này được thực hiện bởi vì các cá thể HttpApplication được sử dụng lại để thực hiện. Khi phiên hết hạn, chỉ sự kiện Session_OnEnd được chỉ định trong tệp Global.asax được thực thi, để ngăn mã gọi trình xử lý sự kiện Kết thúc được liên kết với cá thể HttpApplication hiện đang được sử dụng.

Vì lý do này, việc thêm một HttpModule không có gì là vô nghĩa. Tôi đã thích nghi câu trả lời của Andy thành một SessionScopeAccessor lớp duy nhất:

public class SessionScopeAccessor : IScopeAccessor 
{ 
    private const string Key = "castle.per-web-session-lifestyle-cache"; 

    public void Dispose() 
    { 
     var context = HttpContext.Current; 

     if (context == null || context.Session == null) 
      return; 

     SessionEnd(context.Session); 
    } 

    public ILifetimeScope GetScope(CreationContext context) 
    { 
     var current = HttpContext.Current; 

     if (current == null) 
     { 
      throw new InvalidOperationException("HttpContext.Current is null. PerWebSessionLifestyle can only be used in ASP.Net"); 
     } 

     var lifetimeScope = (ILifetimeScope)current.Session[Key]; 

     if (lifetimeScope == null) 
     { 
      lifetimeScope = new DefaultLifetimeScope(new ScopeCache()); 
      current.Session[Key] = lifetimeScope; 
      return lifetimeScope; 
     } 

     return lifetimeScope; 
    } 

    // static helper - should be called by Global.asax.cs.Session_End 
    public static void SessionEnd(HttpSessionState session) 
    { 
     var scope = (ILifetimeScope)session[Key]; 

     if (scope != null) 
     { 
      scope.Dispose(); 
      session.Remove(Key); 
     } 
    } 
} 

}

Điều quan trọng là để gọi phương thức SessionEnd từ global.asax.cs tập tin của bạn:

void Session_OnEnd(object sender, EventArgs e) 
{ 
    SessionScopeAccessor.SessionEnd(Session); 
} 

Đây là cách duy nhất để xử lý một sự kiện SessionEnd.

Các vấn đề liên quan