1111namespace Xtensive . Orm . Web . Filters
1212{
1313 /// <summary>
14- /// Filter that
14+ /// DataObjects.Net Session providing action filter.
1515 /// </summary>
16- public class SessionToActionProviderFilter : IActionFilter , IAsyncActionFilter
16+ public class SessionActionFilter : IActionFilter , IAsyncActionFilter
1717 {
1818 private SessionAccessor sessionAccessor ;
1919 private IDisposable contextBindResource ;
@@ -38,62 +38,65 @@ public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionE
3838 }
3939
4040 /// <summary>
41- /// Opens session .
41+ /// Opens <see cref="Session"/> .
4242 /// </summary>
43- /// <param name="domain">Domain instance</param>
44- /// <param name="context">Action executing context</param>
43+ /// <param name="domain"><see cref=" Domain"/> instance. </param>
44+ /// <param name="context">Action executing context. </param>
4545 /// <returns>Instance of <see cref="Session"/>.</returns>
4646 protected virtual Session OpenSession ( Domain domain , ActionExecutingContext context ) => domain . OpenSession ( ) ;
4747
4848 /// <summary>
49- /// Opens session asynchronously.
49+ /// Opens <see cref="Session"/> asynchronously.
5050 /// </summary>
51- /// <param name="domain">Domain instance</param>
52- /// <param name="context">Action executing context </param>
51+ /// <param name="domain">A <see cref=" Domain"/> instance. </param>
52+ /// <param name="context">The <see cref="ActionExecutingContext"/>. </param>
5353 /// <returns>Instance of <see cref="Session"/>.</returns>
5454 protected virtual Task < Session > OpenSessionAsync ( Domain domain , ActionExecutingContext context ) => domain . OpenSessionAsync ( ) ;
5555
5656 /// <summary>
57- /// Opens transaction scope .
57+ /// Opens <see cref="TransactionScope"/> .
5858 /// </summary>
59- /// <param name="session">Domain instance .</param>
60- /// <param name="context">Action executing context .</param>
59+ /// <param name="session">The <see cref="Session"/> to open transaction scope .</param>
60+ /// <param name="context">The <see cref="ActionExecutingContext"/> .</param>
6161 /// <returns>Instance of <see cref="TransactionScope"/>.</returns>
6262 protected virtual TransactionScope OpenTransaction ( Session session , ActionExecutingContext context ) =>
6363 session . OpenTransaction ( ) ;
6464
6565 /// <summary>
66- /// Opens transaction scope asynchronously.
66+ /// Opens <see cref="TransactionScope"/> asynchronously.
6767 /// </summary>
68- /// <param name="session">Domain instance .</param>
69- /// <param name="context">Action executing context .</param>
68+ /// <param name="session">The <see cref="Session"/> to open transaction scope .</param>
69+ /// <param name="context">The <see cref="ActionExecutingContext"/> .</param>
7070 /// <returns>Instance of <see cref="TransactionScope"/>.</returns>
7171 protected virtual async Task < TransactionScope > OpenTransactionAsync ( Session session , ActionExecutingContext context ) =>
7272 await session . OpenTransactionAsync ( ) ;
7373
7474 /// <summary>
75- /// Executes before <see cref="TransactionScope "/> dispose .
75+ /// Executes before <paramref name="transactionScope "/> disposing .
7676 /// </summary>
7777 /// <param name="transactionScope">The transaction scope which is about to be disposed.</param>
78- /// <param name="session">The session transaction scope belongs to.</param>
78+ /// <param name="session">The <see cref="Session"/> transaction scope belongs to.</param>
7979 /// <param name="context">The <see cref="ActionExecutedContext"/>.</param>
8080 protected virtual void OnTransactionScopeDisposing ( TransactionScope transactionScope , Session session , ActionExecutedContext context )
8181 {
82+ if ( context . Exception == null || CompleteTransactionOnException ( context . Exception , context ) ) {
83+ transactionScope . Complete ( ) ;
84+ }
8285 }
8386
8487 /// <summary>
85- /// Executes after <see cref="TransactionScope"/> is dispose .
88+ /// Executes after <see cref="TransactionScope"/> is disposed .
8689 /// </summary>
87- /// <param name="session">The session the disposed transaction scope belonged to.</param>
90+ /// <param name="session">The <see cref="Session"/> the disposed transaction scope belonged to.</param>
8891 /// <param name="context">The <see cref="ActionExecutedContext"/>.</param>
8992 protected virtual void OnTransactionScopeDisposed ( Session session , ActionExecutedContext context )
9093 {
9194 }
9295
9396 /// <summary>
94- /// Executes before <see cref="Session "/> dispose .
97+ /// Executes before <paramref name="session "/> disposing .
9598 /// </summary>
96- /// <param name="session">The session which is about to be disposed.</param>
99+ /// <param name="session">The <see cref="Session"/> which is about to be disposed.</param>
97100 /// <param name="context">The <see cref="ActionExecutedContext"/>.</param>
98101 protected virtual void OnSessionDisposing ( Session session , ActionExecutedContext context )
99102 {
@@ -107,18 +110,29 @@ protected virtual void OnSessionDisposed(ActionExecutedContext context)
107110 {
108111 }
109112
113+
114+ /// <summary>
115+ /// Allows to define what exceptions will not leat to transaction rollback.
116+ /// </summary>
117+ /// <param name="exception">The exception thrown by action.</param>
118+ /// <param name="context">The <see cref="ActionExecutedContext"/>.</param>
119+ /// <returns><see langword="true"/> for complete transaction scope, otherwise, <see langword="false"/>.</returns>
120+ protected virtual bool CompleteTransactionOnException ( Exception exception , ActionExecutedContext context ) => false ;
121+
110122 //internal void Persist(PersistReason reason) => Persist(reason, false).GetAwaiter().GetResult();
111123 private async ValueTask ExecuteBeforeAction ( ActionExecutingContext context , bool isAsync )
112124 {
113125 var actionParameters = context . ActionDescriptor . Parameters ;
114126
127+ skipResourcesRelease = true ;
128+
115129 //this search can probably be improved by caching action names with needed parameters
116130 foreach ( var p in actionParameters ) {
117131 if ( p . ParameterType == WellKnownTypes . SessionAccessorType ) {
118132 // trying to get registered accessor as service
119133 var accessor = GetSessionAccesorFromServices ( context . HttpContext ) ;
120134 if ( accessor != null ) {
121- // this is registered as service and probably filled with middlewere
135+ // this is registered as service and probably filled with middleware
122136 skipResourcesRelease = true ;
123137 sessionAccessor = accessor ;
124138 if ( accessor . ContextIsBound ) {
@@ -199,12 +213,10 @@ private async ValueTask<IDisposable> CreateSessionAndBindContext(SessionAccessor
199213 private static Domain GetDomainFromServices ( HttpContext context )
200214 {
201215 var domain = ( Domain ) context . RequestServices . GetService ( WellKnownTypes . DomainType ) ;
202- return domain == null
203- ? throw new InvalidOperationException ( "Domain is not found among registered services." )
204- : domain ;
216+ return domain ?? throw new InvalidOperationException ( "Domain is not found among registered services." ) ;
205217 }
206218
207219 private static SessionAccessor GetSessionAccesorFromServices ( HttpContext context ) =>
208220 ( SessionAccessor ) context . RequestServices . GetService ( WellKnownTypes . SessionAccessorType ) ;
209221 }
210- }
222+ }
0 commit comments