Access Session by ActionFilterAttribute in .NET CORE - .net-core

I tried to access the session-value by using the ActionFilterAttribute in .NET CORE. Therefore i use the following code:
A normal .NET Core Controller which gets the annotation [SessionCheck].
[SessionCheck]
public class HomeController : Controller
{
}
These class is extended by the ActionFilterAttribute Class and overrides the OnActionExecuted Method, where i would like to access the session value. Is there a possibility? I could only access the Key but not the session value.
public class SessionCheck:ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext context)
{
var session = context.HttpContext.Session;
//var mySessionValue = session["user"];
}
}
My main problem is, that i have a session value which describes where i have to route my request. If there is a special value in, i would like to route my request to Location A, otherwise to Location B.
Is there another possibility to do that?

I have same issue, you can do something like below:
filterContext.HttpContext.Session.GetString("user");

public class SessionCheck : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext actionExecutingContext)
{
if (actionExecutingContext.HttpContext.Session.GetString("user") == "User One")
{
actionExecutingContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "LocationA Controller",
action = "LocationA Action",
returnurl = Microsoft.AspNetCore.Http.Extensions.UriHelper.GetEncodedUrl(actionExecutingContext.HttpContext.Request)
}));
}
else
{
actionExecutingContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "LocationB Controller",
action = "LocationB Action",
returnurl = Microsoft.AspNetCore.Http.Extensions.UriHelper.GetEncodedUrl(actionExecutingContext.HttpContext.Request)
}));
}
}
}
Write SessionCheck class as above you can set your special value by typing HttpContext.Session.SetString("user", "User One"); in where you want to set also you can use multiple if-elseif like the same, This is working in .Net Core 3.1.

Related

Check session before running controller issue in ASP.NET core

So I want to check if a session exists before running any action in the controller. I found these solutions link1,link2 and tried implementing them.
This is my SessionAuthorize class, everything seems fine here:
public class SessionAuthorize : ActionFilterAttribute
{
private readonly SessionManager _sessionManager;
public SessionAuthorize(SessionManager sessionManager)
{
_sessionManager = sessionManager;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!_sessionManager.IsLoggedIn)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary {
{ "Controller", "Home" },
{ "Action", "Login" }
});
}
}
}
Now, the issue am having is when I try to call it in the controller. So I am supposed to call by adding the [SessionAuthorize] attribute like this:
[SessionAuthorize]
public class AccountController : Controller
{
//code
}
But it gives me the error that I am not calling it properly. I believe it has to do with the dependency injection I used in SessionAuthorize to access the session. But I am not sure how to call it now.
The error message is:
'There is no argument given that corresponds to the required formal
parameter 'sessionManager' of
'SessionAuthorize.SessionAuthorize(SessionManager)'
So I would like to know how am I supposed to use it in my controller
If you want to avoid the Service Locator pattern you can use DI by constructor injection with a TypeFilter.
In your controller try using
[TypeFilter(typeof(SessionAuthorize)]
And your ActionFilterAttribute does not need to access a service provider instance anymore.

How create custom authorization attribute for checking a role and url path in Asp.Net Core?

I want to create a custom authorization attribute for checking the role and url path.
I've find the way for doing it in the Asp.Net Core using the Policy-Based-Authorization but I've tried implement it but I can't get the HttpContext with incomming url.
AuthorizationHandlerContext hasn't access to HttpContext probable.
How can I get current HttpContext with the url path? Is it possible to do that or with another way?
I've tried this code for creating the custom Policy:
public class RoleUrlValidationHandler : AuthorizationHandler<RoleUrlValidationRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RoleUrlValidationRequirement requirement)
{
var path = //Here I need get current url path for example - /api/posts/4545411
var pathPart = path.Split('/');
var clientId = pathPart[3];
if (context.User.IsInRole(clientId))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
I want to create following:
[Authorize(Policy="RoleUrlValidation")] //Get ClientId from Url and check User's roles
public class PostsController : Controller
{
public ActionResult Get()
{
}
}
The policy approach is the right one. Only bit you missed is, that you can use Dependency Injection in the Handlers.
public class RoleUrlValidationHandler : AuthorizationHandler<RoleUrlValidationRequirement>
{
private readonly IHttpContextAccessor contextAccessor;
public RoleUrlValidationHandler(IHttpContextAccessor contextAccessor)
{
this.contextAccessor = contextAccessor;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RoleUrlValidationRequirement requirement)
{
var httpContext = contextAccessor.HttpContext;
var path = httpContext.Request.Path;
var pathPart = path.Split('/');
var clientId = pathPart[3];
if (context.User.IsInRole(clientId))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
You also may have to register the IHttpContextAccessor as its not registered by default.
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Extra bits:
Consider using var routeData = httpContext.GetRouteData() instead of using path.Split('/') for reading values from it so you can easily read the parameter values from the route.
Try like this:
((DefaultHttpContext)context.Resource).Request.Path.Value

ASP.NET Web API Controller Specific Serializer

I've a self host Web API with 2 controllers:
For controller 1, I need default DataContractSerializer (I'm exposing EF 5 POCO)
For controller 2, I need XmlFormatter with parameter UseXmlSerializer set to true (I'm exposing an XmlDocument)
I've tried to set formatters during controller initialization, but the configuration seems to be global, affecting all controllers:
public class CustomConfigAttribute : Attribute, IControllerConfiguration
{
public void Initialize(HttpControllerSettings settings,
HttpControllerDescriptor descriptor)
{
settings.Formatters.XmlFormatter.UseXmlSerializer = true;
}
}
How can I solve this?
You were very much on the right track. But you need to initallise a new instance of the XmlMediaTypeFormatter in your config attributes otherwise you will affect the global reference.
As you know, you need to create 2 attributes based on the IControllerConfiguration interface.
public class Controller1ConfigAttribute : Attribute, IControllerConfiguration
{
public void Initialize(HttpControllerSettings controllerSettings,
HttpControllerDescriptor controllerDescriptor)
{
var xmlFormater = new XmlMediaTypeFormatter {UseXmlSerializer = true};
controllerSettings.Formatters.Clear();
controllerSettings.Formatters.Add(xmlFormater);
}
}
public class Controller2ConfigAttribute : Attribute, IControllerConfiguration
{
public void Initialize(HttpControllerSettings controllerSettings,
HttpControllerDescriptor controllerDescriptor)
{
var xmlFormater = new XmlMediaTypeFormatter();
controllerSettings.Formatters.Clear();
controllerSettings.Formatters.Add(xmlFormater);
}
}
Then decorate your controllers with the relevant attribute
[Controller1ConfigAttribute]
public class Controller1Controller : ApiController
{
[Controller2ConfigAttribute]
public class Controller2Controller : ApiController
{
Configuration:
config.Formatters.Remove(config.Formatters.JsonFormatter);
config.Formatters.Insert(0, new CustomXmlMediaTypeFormatter());
The Custom formatter:
public class CustomXmlMediaTypeFormatter : XmlMediaTypeFormatter
{
public CustomXmlMediaTypeFormatter()
{
UseXmlSerializer = true;
}
}
This seems to work, ok not so elegant.
Removing default Xml Formatter does not work,
so I concluded that the framework is somehow still using it.
Mark Jones' answer has a big downside: By clearing all formatters it is not possible to request different ContentTypes and make use of the relevant formatter.
A better way to enable the XMLSerializer per Controller is to replace the default formatter.
public class UseXMLSerializerAttribute : Attribute, IControllerConfiguration
{
public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
{
// Find default XMLFormatter
var xmlFormatter = controllerSettings.Formatters.FirstOrDefault(c => c.SupportedMediaTypes.Any(x => x.MediaType == "application/xml"));
if (xmlFormatter != null)
{
// Remove default formatter
controllerSettings.Formatters.Remove(xmlFormatter);
}
// Add new XMLFormatter which uses XmlSerializer
controllerSettings.Formatters.Add(new XmlMediaTypeFormatter { UseXmlSerializer = true });
}
}
And use it like this:
[UseXMLSerializer]
public TestController : ApiController
{
//Actions
}
I think you could write a custom ActionFilterAttribute.
In OnActionExecuting, store away the original values in the HttpContext and then in OnActionExecuted, restore the original values.
the controllers actions themselves should not be concerned with how the data is serialized. yo should be able to request the data and any format necessary the operation to retrieve the data would be the same.
by default web api serialized to json objects. however if you set the content type of the request to xml is should return the same result, but formatted as xml instead of json.

Using Forms Authentication with Web API

I've got a Web Forms application which I'm trying to use the new Web API beta with. The endpoints I'm exposing should only be available to an authenticated user of the site since they're for AJAX use. In my web.config I have it set to deny all users unless they're authenticated. This works as it should with Web Forms but does not work as expected with MVC or the Web API.
I've created both an MVC Controller and Web API Controller to test with. What I'm seeing is that I can't access the MVC or Web API endpoints untill I authenticate but then I can continue hitting those endpoints, even after closing my browser and recyling the app pool. But if I hit one of my aspx pages, which sends me back to my login page, then I can't hit the MVC or Web API endpoints untill I authenticate again.
Is there a reason why MVC and Web API are not functioning as my ASPX pages are once my session is invalidated? By the looks of it only the ASPX request is clearing my Forms Authentication cookie, which I'm assuming is the issue here.
If your web API is just used within an existing MVC application, my advice is to create a custom AuthorizeAttribute filter for both your MVC and WebApi controllers; I create what I call an "AuthorizeSafe" filter, which blacklists everything by default so that if you forget to apply an authorization attribute to the controller or method, you are denied access (I think the default whitelist approach is insecure).
Two attribute classes are provided for you to extend; System.Web.Mvc.AuthorizeAttribute and System.Web.Http.AuthorizeAttribute; the former is used with MVC forms authentication and the latter also hooks into forms authentication (this is very nice because it means you don't have to go building a whole separate authentication architecture for your API authentication and authorization). Here's what I came up with - it denies access to all MVC controllers/actions and WebApi controllers/actions by default unless an AllowAnonymous or AuthorizeSafe attribute is applied. First, an extension method to help with custom attributes:
public static class CustomAttributeProviderExtensions {
public static List<T> GetCustomAttributes<T>(this ICustomAttributeProvider provider, bool inherit) where T : Attribute {
List<T> attrs = new List<T>();
foreach (object attr in provider.GetCustomAttributes(typeof(T), false)) {
if (attr is T) {
attrs.Add(attr as T);
}
}
return attrs;
}
}
The authorization helper class that both the AuthorizeAttribute extensions use:
public static class AuthorizeSafeHelper {
public static AuthActionToTake DoSafeAuthorization(bool anyAllowAnonymousOnAction, bool anyAllowAnonymousOnController, List<AuthorizeSafeAttribute> authorizeSafeOnAction, List<AuthorizeSafeAttribute> authorizeSafeOnController, out string rolesString) {
rolesString = null;
// If AllowAnonymousAttribute applied to action or controller, skip authorization
if (anyAllowAnonymousOnAction || anyAllowAnonymousOnController) {
return AuthActionToTake.SkipAuthorization;
}
bool foundRoles = false;
if (authorizeSafeOnAction.Count > 0) {
AuthorizeSafeAttribute foundAttr = (AuthorizeSafeAttribute)(authorizeSafeOnAction.First());
foundRoles = true;
rolesString = foundAttr.Roles;
}
else if (authorizeSafeOnController.Count > 0) {
AuthorizeSafeAttribute foundAttr = (AuthorizeSafeAttribute)(authorizeSafeOnController.First());
foundRoles = true;
rolesString = foundAttr.Roles;
}
if (foundRoles && !string.IsNullOrWhiteSpace(rolesString)) {
// Found valid roles string; use it as our own Roles property and auth normally
return AuthActionToTake.NormalAuthorization;
}
else {
// Didn't find valid roles string; DENY all access by default
return AuthActionToTake.Unauthorized;
}
}
}
public enum AuthActionToTake {
SkipAuthorization,
NormalAuthorization,
Unauthorized,
}
The two extension classes themselves:
public sealed class AuthorizeSafeFilter : System.Web.Mvc.AuthorizeAttribute {
public override void OnAuthorization(AuthorizationContext filterContext) {
if (!string.IsNullOrEmpty(this.Roles) || !string.IsNullOrEmpty(this.Users)) {
throw new Exception("This class is intended to be applied to an MVC web API application as a global filter in RegisterWebApiFilters, not applied to individual actions/controllers. Use the AuthorizeSafeAttribute with individual actions/controllers.");
}
string rolesString;
AuthActionToTake action = AuthorizeSafeHelper.DoSafeAuthorization(
filterContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>(false).Count() > 0,
filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>(false).Count() > 0,
filterContext.ActionDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>(false),
filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>(false),
out rolesString
);
string rolesBackup = this.Roles;
try {
switch (action) {
case AuthActionToTake.SkipAuthorization:
return;
case AuthActionToTake.NormalAuthorization:
this.Roles = rolesString;
base.OnAuthorization(filterContext);
return;
case AuthActionToTake.Unauthorized:
filterContext.Result = new HttpUnauthorizedResult();
return;
}
}
finally {
this.Roles = rolesBackup;
}
}
}
public sealed class AuthorizeSafeApiFilter : System.Web.Http.AuthorizeAttribute {
public override void OnAuthorization(HttpActionContext actionContext) {
if (!string.IsNullOrEmpty(this.Roles) || !string.IsNullOrEmpty(this.Users)) {
throw new Exception("This class is intended to be applied to an MVC web API application as a global filter in RegisterWebApiFilters, not applied to individual actions/controllers. Use the AuthorizeSafeAttribute with individual actions/controllers.");
}
string rolesString;
AuthActionToTake action = AuthorizeSafeHelper.DoSafeAuthorization(
actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0,
actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0,
actionContext.ActionDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>().ToList(),
actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>().ToList(),
out rolesString
);
string rolesBackup = this.Roles;
try {
switch (action) {
case AuthActionToTake.SkipAuthorization:
return;
case AuthActionToTake.NormalAuthorization:
this.Roles = rolesString;
base.OnAuthorization(actionContext);
return;
case AuthActionToTake.Unauthorized:
HttpRequestMessage request = actionContext.Request;
actionContext.Response = request.CreateResponse(HttpStatusCode.Unauthorized);
return;
}
}
finally {
this.Roles = rolesBackup;
}
}
}
And finally, the attribute that can be applied to methods/controllers to allow users in certain roles to access them:
public class AuthorizeSafeAttribute : Attribute {
public string Roles { get; set; }
}
Then we register our "AuthorizeSafe" filters globally from Global.asax:
public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
// Make everything require authorization by default (whitelist approach)
filters.Add(new AuthorizeSafeFilter());
}
public static void RegisterWebApiFilters(HttpFilterCollection filters) {
// Make everything require authorization by default (whitelist approach)
filters.Add(new AuthorizeSafeApiFilter());
}
Then to open up an action to eg. anonymous access or only Admin access:
public class AccountController : System.Web.Mvc.Controller {
// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login(string returnUrl) {
// ...
}
}
public class TestApiController : System.Web.Http.ApiController {
// GET API/TestApi
[AuthorizeSafe(Roles="Admin")]
public IEnumerable<TestModel> Get() {
return new TestModel[] {
new TestModel { TestId = 123, TestValue = "Model for ID 123" },
new TestModel { TestId = 234, TestValue = "Model for ID 234" },
new TestModel { TestId = 345, TestValue = "Model for ID 345" }
};
}
}
It should work in Normal MVC controller. you just need to decorate the action with [Authorize] attribute.
In web api you need to have custom authorization. you may find below link helpful.
http://www.codeproject.com/Tips/376810/ASP-NET-WEB-API-Custom-Authorize-and-Exception-Han
If you are using the MVC Authorize attribute it should work the same way on for the WebAPI as for normal MVC controllers.

Disable Session state per-request in ASP.Net MVC

I am creating an ActionResult in ASP.Net MVC to serve images. With Session state enabled, IIS will only handle one request at a time from the same user. (This is true not just in MVC.)
Therefore, on a page with multiple images calling back to this Action, only one image request can be handled at a time. It's synchronous.
I'd like this image Action to be asynchronous -- I'd like multiple image requests to each execute without needing the previous one to complete. (If the images were just static files, IIS would serve them up this way.)
So, I'd like to disable Session just for calls to that Action, or to specify that certain requests do not have Session state. Anyone know how this is done in MVC? Thanks!
If anyone is in the situation I was in, where your image controller actually needs read only access to the session, you can put the SessionState attribute on your controller
[SessionState(SessionStateBehavior.ReadOnly)]
See http://msdn.microsoft.com/en-us/library/system.web.mvc.sessionstateattribute.aspx for more info.
Thanks to https://stackoverflow.com/a/4235006/372926
Rather than implementing an action filter for this, why don't you implement a RouteHandler?
Here's the deal - IRouteHandler has one method - GetHttpHandler. When you make an ASP.Net MVC request to a controller, by default the routing engine handles the request by creating a new instance of MvcRouteHandler, which returns an MvcHandler. MvcHandler is an implementation of IHttpHandler which is marked with the (surprise!) IRequiresSessionState interface. This is why a normal request uses Session.
If you follow my blog post on how to implement a custom RouteHandler (instead of using MvcRouteHandler) for serving up images - you can skip returning a session-tagged IHttpHandler.
This should free IIS from imposing synchronicity on you. It would also likely be more performant because it's skipping all the layers of the MVC code dealing with filters.
I also came across the same problem and after doing R&D this link worked for me
Reference:
https://techatfingers.wordpress.com/2016/06/14/session-state-on-action/
Create custom Attribute
Override the “GetControllerSessionBehavior” method present in class DefaultControllerFactory.
Register it in global.aspx
1> Create custom Attribute
public sealed class ActionSessionStateAttribute : Attribute
{
public SessionStateBehavior SessionBehavior { get; private set; }
public ActionSessionStateAttribute(SessionStateBehavior sessionBehavior)
{
SessionBehavior = sessioBehavior;
}
}
2. Override
public class SessionControllerFactory : DefaultControllerFactory
{
protected override SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
return SessionStateBehavior.Default;
var actionName = requestContext.RouteData.Values["action"].ToString();
Type typeOfRequest=requestContext.HttpContext.Request.RequestType.ToLower() =="get"?typeof(HttpGetAttribute):typeof(HttpPostAttribute);
// [Line1]
var cntMethods = controllerType.GetMethods()
.Where(m =>
m.Name == actionName &&
( ( typeOfRequest == typeof(HttpPostAttribute) &&
m.CustomAttributes.Where(a => a.AttributeType == typeOfRequest).Count()>0
)
||
( typeOfRequest == typeof(HttpGetAttribute) &&
m.CustomAttributes.Where(a => a.AttributeType == typeof(HttpPostAttribute)).Count() == 0
)
)
);
MethodInfo actionMethodInfo = actionMethodInfo = cntMethods != null && cntMethods.Count() == 1 ? cntMethods.ElementAt(0):null;
if (actionMethodInfo != null)
{
var sessionStateAttr = actionMethodInfo.GetCustomAttributes(typeof(ActionSessionStateAttribute), false)
.OfType<ActionSessionStateAttribute>()
.FirstOrDefault();
if (sessionStateAttr != null)
{
return sessionStateAttr.Behavior;
}
}
return base.GetControllerSessionBehavior(requestContext, controllerType);
}
3. Register class in Global.asax
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// --- other code ---
ControllerBuilder.Current.SetControllerFactory(typeof(SessionControllerFactory));
}
}
Try serving the images from another domain. So something like images.mysite.com.
This will provide you two benefits: One, sessions are tracked by a cookie, so images.mysite.com won't have the cookie. Two, it will give you an additional two concurrent requests to retrieve images.
Have you considered setting up a HttpHandler to serve up your images?
SessionState attribute is quite helpful if u use mvc3. How to achieve this with mvc2 needs a little more coding.
Idea is to tell the asp.net that specific request wont use session object.
So, Create a custom route handler for specific requests
public class CustomRouteHandler : IRouteHandler
{
public System.Web.IHttpHandler GetHttpHandler(RequestContext requestContext)
{
requestContext.HttpContext.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.ReadOnly);
return new MvcHandler(requestContext);
}
}
SessionStateBehavior enum has 4 members, you should use "disabled" or "readonly" modes to get async behavior.
After creating this custom route handler, be sure that your specific requests goes through this handler. This can be done via defining new routes at Global.asax
routes.Add("Default", new Route(
"{controller}/{action}",
new RouteValueDictionary(new { controller = "Home", action = "Index"}),
new CustomRouteHandler()
));
Adding this route makes all your requests to be handled by your custom route handler class. You can make it specific by defining different routes.
Change DefaultCOntrollerFactory to custom ControllerFactory class. Default Controller.TempDataProvider use SessionStateTempDataProvider. you can change it.
1.Set web.config/system.web/sessionState:mode="Off".
2.create DictionaryTempDataProvider class.
public class DictionaryTempDataProvider : ITempDataProvider
{
public IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
{
return new Dictionary<string, object>();
}
public void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
{
}
}
3.Create DictionaryTempDataControllerFactory
public class DictionaryTempDataControllerFactory : DefaultControllerFactory
{
public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
var controller = base.CreateController(requestContext, controllerName) as Controller;
if (controller!=null)
controller.TempDataProvider = new DictionaryTempDataProvider();
return controller;
}
}
4.In global.asax.cs Apprication_Start event set DictionaryTempDataControllerFactory.
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(
new DictionaryTempDataControllerFactory()
);
}
On our server, IIS doesn't even know about sessions - it's the ASP.NET stack that handles one request per session at a time. Static files, like images, are never affected.
Is it possible that your ASP.NET app is serving the files instead of IIS?
Create new Controller
Decorate controler with [SessionState(SessionStateBehavior.Disabled)]
Refactor code you want seesion stated disabled for to that controller
I would to share my solution for disable ASP.NET Session for an specific request (in my case, a WCF Service) using an HttpModule:
public class AspNetSessionFilterModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PostMapRequestHandler += OnPostMapRequestHandler;
}
private void OnPostMapRequestHandler(object sender, EventArgs e)
{
var context = (sender as HttpApplication).Context;
DisableSessionForSomeRequests(context);
}
private void DisableSessionForSomeRequests(HttpContext context)
{
if ("~/Services/MyService.svc".Equals(context.Request.AppRelativeCurrentExecutionFilePath, StringComparison.InvariantCultureIgnoreCase))
{
context.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Disabled);
}
}
public void Dispose()
{ }
}

Resources