1- ================
1+ ================
22Xtensive.Orm.Web
33================
44
55Summary
66-------
7- The extension adds integration for DataObjects.Net Core and ASP.NET Core. It contains SessionManager class
8- which is middleware and can be used as part of ASP.NET Core Pipeline. SessionManager opens session and transaction on going down the pipeline
9- and disposes them on going up the pipeline.
7+ The extension adds integration for DataObjects.Net and ASP.NET Core. It contains an action filter called SessionActionFilter
8+ and a middleware called OpenSessionMiddleware. The action filter is useful for providing session per MVC action. The middleware,
9+ though, has wider coverage and can provide session to actions, controllers, razor pages and to other middleware down the pipeline.
10+ Both of them open session and transaction and at the end dispose them. As obsolete SessionManager, they complete transacton scope
11+ by default unless an exeption appeared. (more info on https://dataobjects.net)
1012
11- SessionManager has the following features:
12- 1. When Session.Current is accessed, and there is no current Session, it will provide a new instance of Session.
13- In that case a new transaction will be created. It will be committed when the pipeline execution returns to SessionManager without any exception,
14- otherwise it will be rolled back.
15- 2. Setting SessionManager.Demand().HasErrors to true will lead to rollback of this transaction.
16- 3. SessionManager.Current (and SessionManager.Demand()) returns the instance of SessionManager
17- bound to the current pipeline execution, i.e. current SessionManager.
18- Its Session property (if not null) is the same value as the one provided by Session.Current.
13+ Prerequisites
14+ -------------
15+ DataObjects.Net 7 or later (https://dataobjects.net)
1916
20- Note that presence of SessionManager does not prevent you from creating Sessions manually.
21- It operates relying on Session.Resolver event, which is raised only when there is no current Session.
17+ Usage of action filter
18+ ----------------------
2219
23- Finally, no automatic Session + transaction will be provided, if you don't use Session.Current/Session.Demand() methods
24- in your code (directly or indirectly). So e.g. requests to static web pages won't lead to any DB interaction.
20+ To start using action filter it should be added to action filters collection like so
2521
26- Prerequisites
27- -------------
28- DataObjects.Net Core 0.1 or later (http://dataobjects.net)
22+ public class Startup
23+ {
24+ public Startup(IConfiguration configuration)
25+ {
26+
27+ }
2928
30- Implementation
31- --------------
32- To start using SessionManager it should be added to ASP.NET Middleware pipeline in Startup class like in example below
29+ public void ConfigureServices(IServiceCollection services)
30+ {
31+ var domain = BuildDomain();
32+
33+ // Domain should be available as service to have
34+ // access to it from action filter.
35+ services.AddSingleton<Domain>(domain);
36+
37+ // Adds SessionAccessor as scoped service (one instance per request).
38+ // Session accessor will be able to access Session and TransactionScope
39+ // instances which are in HttpContext
40+ services.AddDataObjectsSessionAccessor();
41+
42+ // Adds the action filter
43+ services.AddControllers(options => options.Filters.AddDataObjectsSessionActionFilter());
44+ }
45+
46+ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
47+ {
48+ if (env.IsDevelopment()) {
49+ app.UseDeveloperExceptionPage();
50+ }
51+ else {
52+ app.UseExceptionHandler("/Home/Error");
53+ }
54+ app.UseStaticFiles()
55+ .UseRouting()
56+ .UseAuthorization()
57+ .UseEndpoints(endpoints => {
58+ endpoints.MapControllerRoute(
59+ name: "default",
60+ pattern: "{controller=Home}/{action=Index}/{id?}");
61+ });
62+ }
63+ }
64+
65+
66+ After action filter is added you can use it like in the example bellow
67+
68+ public class HomeController : Controller
69+ {
70+ // If action require Session and TransactionScope to be opened then
71+ // just put parameter like in this method.
72+ // Action filter will find it wrap action with session
73+ public IActionResult Index([FromServices] SessionAccessor sessionAccessor)
74+ {
75+ var sessionInstance = sessionAccessor.Session;
76+ var transactionScopeInstance = sessionAccessor.TransactionScope;
77+
78+ // some queries to database
79+
80+ return View();
81+ }
82+
83+ // If action does not require opened Session
84+ // then don't put SessionAccessor as parameter
85+ // action filter will skip openings
86+ public IActionResult Privacy()
87+ {
88+ return View();
89+ }
90+ }
91+
92+
93+ Usage of Middleware
94+ -------------------
95+
96+ The middleware is needed to be placed to pipeline before any other middleware that require access
97+ to session. Pipeline may be configured like so
3398
3499public class Startup
35100{
@@ -40,28 +105,132 @@ public class Startup
40105
41106 public void ConfigureServices(IServiceCollection services)
42107 {
43- // Configure services
108+ var domain = BuildDomain();
109+
110+ // Domain should be available as service to have
111+ // access to it from action filter.
112+ services.AddSingleton<Domain>(domain);
113+
114+ // Adds SessionAccessor as scoped service (one instance per request).
115+ // Session accessor will be able to access Session and TransactionScope
116+ // instances which are in HttpContext
117+ services.AddDataObjectsSessionAccessor();
118+
119+ // Adds the action filter
120+ services.AddControllers();
44121 }
45122
46123 public void Configure(IApplicationBuilder app, IHostingEnvironment env)
47124 {
48- // Configure parts of the pipeline which are before SessionManager.
49- // It will be unable to use SessionManager functionality in these parts
50- // For instance,
125+ if (env.IsDevelopment()) {
126+ app.UseDeveloperExceptionPage();
127+ }
128+ else {
129+ app.UseExceptionHandler("/Home/Error");
130+ }
51131 app.UseStaticFiles()
132+ .UseRouting()// this middleware won't have opened session
133+ .UseDataObjectsSessionOpener()// open Session and Transaction scope
134+ .UseAuthorization()// this middleware and the rest down the pipe have Session access
135+ .UseEndpoints(endpoints => {
136+ endpoints.MapControllerRoute(
137+ name: "default",
138+ pattern: "{controller=Home}/{action=Index}/{id?}");
139+ });
140+ }
141+ }
142+
143+ After that you can access opened Session and TransactionScope
52144
53- // Add session manager to the pipeline
54- app.UseSessionManager();
55-
56- // Configure parts of the pipeline which are after SessionManager.
57- // These parts will work with SessionManager.
58-
59- // For instance, MVC controllers will be able to query data using DataObjects.Net
60- app.UseMvc(routes =>
145+ public class HomeController : Controller
146+ {
147+ // access from controller's constructor
148+ public HomeController(SessionAccessor sessionAccessor)
149+ {
150+ // some work
151+ }
152+
153+ // access from action
154+ public IActionResult Index([FromServices] SessionAccessor sessionAccessor)
155+ {
156+ var sessionInstance = sessionAccessor.Session;
157+ var transactionScopeInstance = sessionAccessor.TransactionScope;
158+
159+ // some queries to database
160+
161+ return View();
162+ }
163+
164+ // NOTE that here session is opened too,
165+ // even there is no SessionAccessor as parameter
166+ // this is the difference in work of middleware and action filter
167+ public IActionResult Privacy()
168+ {
169+ return View();
170+ }
171+ }
172+
173+
174+ The middleware is also usable in Razor Pages projects. In this case
175+ your Startup class may look like this:
176+
177+ public class Startup
178+ {
179+ public Startup(IConfiguration configuration)
180+ {
181+ Configuration = configuration;
182+ }
183+
184+ public IConfiguration Configuration { get; }
185+
186+ // This method gets called by the runtime. Use this method to add services to the container.
187+ public void ConfigureServices(IServiceCollection services)
188+ {
189+ var domain = Domain.Build();
190+ services.AddSingleton(domain);
191+ services.AddDataObjectsSessionAccessor();
192+ services.AddRazorPages();
193+ }
194+
195+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
196+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
197+ {
198+ if (env.IsDevelopment()) {
199+ app.UseDeveloperExceptionPage();
200+ }
201+ else {
202+ app.UseExceptionHandler("/Error");
203+ }
204+
205+ app.UseStaticFiles();
206+
207+ app.UseRouting();
208+
209+ app.UseAuthorization();
210+
211+ app.UseDataObjectsSessionOpener();
212+
213+ app.UseEndpoints(endpoints =>
61214 {
62- routes.MapRoute(
63- name: "default",
64- template: "{controller=Home}/{action=Index}/{id?}");
215+ endpoints.MapRazorPages();
65216 });
66217 }
218+ }
219+
220+ And then in actual pages you can use SessionAccessor like below
221+
222+ public class IndexModel : PageModel
223+ {
224+
225+ public IndexModel(SessionAccessor accessor)
226+ {
227+ _logger = logger;
228+ }
229+
230+ public void OnGet([FromServices] SessionAccessor sessionAccessor)
231+ {
232+ var sessionInstance = sessionAccessor.Session;
233+
234+ // query some data from database
235+ }
67236}
0 commit comments