How to create an object once and use it several times in Controller - asp.net

I want to populate an object using methods in a Controller class. So I've created the object in the Controller class and then tried to populate it using methods in the class. This does not work though, because everytime a method in a controller is called, the entire Controller class is reinitiated. So I get a brand new object everytime I try to populate the object. However, I don't know how else I can create an object and populate it. I've added the code below. The object I'm talking about is ProcessModel. Ignore the other objects I created at the top of the class.
Controller:
public class HomeController : Controller
{
ProcessModel pm = new ProcessModel();
RetrievePatterns pt = new RetrievePatterns();
RetrieveModel rm = new RetrieveModel();
public IActionResult Index()
{
FindPatterns fp = new FindPatterns(rm.pm, pt.KpiPatterns);
ViewData["KPIs"] = fp.passdata;
return View();
}
[HttpPost]
public IActionResult AddEvent([FromBody] Event data)
{
data.ID = pm.EventObjects.Count + 1;
pm.EventObjects.Add(data);
return Json(pm.EventObjects.Count);
}
[HttpPost]
public IActionResult AddProcessName(string data)
{
pm.ID = 1;
pm.Name = data;
return Json(new { title = pm.Name });
}
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
return View();
}
public IActionResult Contact()
{
ViewData["Message"] = "Your contact page.";
return View();
}
public ActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
Object:
public class ProcessModel
{
public List<Event> EventObjects = new List<Event>();
public List<Entity> EntityObjects = new List<Entity>();
public int ID { get; set; }
public string Name { get; set; }
}

You could create a Singleton class which is responsible to create only one instance from Process model;
public class ProcessModelSingleton
{
private static ProcessModel _processModel = new ProcessModel();
private ProcessModelSingleton()
{
}
public static ProcessModel Instance
{
get { return _processModel; }
}
}
Then assign it a global variable in the controller;
ProcessModel pm = ProcessModelSingleton.Instance;
EDIT
Created instance by singleton class shared by all users. So, if you want to make it user specific, using Session is the best option.
public ProcessModel pm {
get
{
if (Session["ProcessModel"] == null)
{
Session["ProcessModel"] = new ProcessModel();
}
return (ProcessModel)Session["ProcessModel"];
}
}

Keep in mind static variables are shared across all users for that web server. So data populated into a singleton class by one user's request can be read/overwritten by a different user's request. This may be what you are after, but if not you should look into Session State.
ASP.NET Session State Overview
As HTTP is stateless by nature, usage of sessions are discouraged my Microsoft for Asp.NET. By default they are only visible to the web server that creates them so if you are using a farm, you either need to look at server affinity (also called sticky sessions) with a load balancer or out of proc state (such as SQL Server or NCache).
Ideally your application design should avoid the need for sessions and carry only the necessary data between requests via cookies and/or query strings. For more complex applications however this is usually not possible and so Session State is used.

Related

Creating a Subclass of IdentityUser on Indentity framework .NET MVC 6,

I have created this class to identify the users who work on a laboratory, with the lab they work on, the class extends from User, which also extends form IdentityUser.
namespace ASP6_SinAuth.Models
{
[Table("Laboratory_workers")]
public class LaboratoryWorker : User
{
[Column("id_lab")]
public Laboratory? laboratory { get; set; }
}
}
Then I created a controller wuth a creation function like for a normal model:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,laboratory")] LaboratoryWorker worker,int laboratory)
{
if (worker.Id != null)
{
worker.laboratory = _context.Laboratory.Find(laboratory);
_context.Add(worker);
_context.SaveChanges();
return RedirectToAction("Index");
} else
{
Console.WriteLine("Error creatring the worker ");
}
return View(worker);
}
But here comes the problem, the instance of LaboratoryWorker is from a new User and i dont know how to turn a normal User instance to a LaboratoryWorker so I can save it in the DB.

IConfigureOptions<T> is not creating scoped options

Typically Options are singleton. However i am building options from the database, and one of the Options property is password which keep changing every month. So i wanted to create Scoped instance of Options. I am using IConfigureOptions<T> like below to build Options from the database
public class MyOptions
{
public string UserID {get;set;}
public string Password {get;set;
}
public class ConfigureMyOptions : IConfigureOptions<MyOptions>
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public ConfigureMyOptions(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public void Configure(MyOptions options)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var provider = scope.ServiceProvider;
using (var dbContext = provider.GetRequiredService<MyDBContext>())
{
options.Configuration = dbContext.MyOptions
.SingleOrDefault()
.Select(x => new MyOptions()
{
UserID = x.UserID,
Password = x.Password
});
}
}
}
}
Use it in controller
public class HomeController : BaseController
{
private readonly MyOptions _options;
public HomeController(IOptions<MyOptions> option)
{
_options = option.Value;
}
[HttpGet]
[Route("home/getvalue")]
public string GetValue()
{
// do something with _options here
return "Success";
}
}
I want to create an instance of MyOptions for every new request so register it as Scoped in startup.cs
services.AddScoped<IConfigureOptions<MyOptions>, ConfigureMyOptions>();
However, when i put debugger inside ConfigureMyOptions's Configure method it only gets hit once for the first request. For next request onward the container returns the same instance (like singleton).
How do i set the scope here so MyOptions will get created for each request?
Use IOptionsSnapshot instead of IOptions in your controller and it will recreate options per request.
Why doesn't work with IOptions:
.AddOptions extension method of Configuration API registers the OptionsManager instance as a singlethon for IOptions<>
services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(OptionsManager<>)));
services.TryAdd(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot<>), typeof(OptionsManager<>)));
and OptionsManager class uses caching internally:
public virtual TOptions Get(string name)
{
name = name ?? Options.DefaultName;
// Store the options in our instance cache
return _cache.GetOrAdd(name, () => _factory.Create(name));
}
The following issue on github helped to find above: OptionsSnapshot should always be recreated per request

Asp.net Identity DbContext / Repository Issue

I am using Asp.Net identity within my MVC app. I can see that this has it's own ApplicationDbContext - albeit it is connected to the same SQL db as my own DbContext I am using elsewhere.
So I am trying to access some of my own data via my own code within the AccountController - it does not seem to work I presume because of some confusion over which DBContext it thinks is active?
My Code :
public class AccountController : Controller
{
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
private PostageManager postmgr;
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, PostageManager _postmgr)
{
UserManager = userManager;
SignInManager = signInManager;
postmgr = _postmgr;
}
public ApplicationSignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set
{
_signInManager = value;
}
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
{
//create select list items for countries drop down
List<SelectListItem> countries;
countries = postmgr.GetCountries().Select(item => new SelectListItem
{
Value = item.Country,
Text = item.Country
}).ToList();
countries.Insert(0, new SelectListItem { Value = string.Empty, Text = "Select delivery country or region...", Selected = true });
RegisterViewModel mode = new RegisterViewModel
{
Countries = countries
};
return View();
}
}
}
PostageManager is just a class that sits over my DAL to fetch some data (which uses repository pattern) - I'm using just a kind of pass through method to grab a list of countries, and using it in exactly the same way I have in other controllers which works fine. Underneath that class is my repository code that is linked to my default connection string (DBContext). It's balking at the following line with a null reference exception, I think postmgr is null :
countries = postmgr.GetCountries().Select(item => new SelectListItem
In reverse to get access to the identity data within my own controllers I have done the following :
public BasketController(BasketManager _mgr, PostageManager _postmgr, ProductManager _prodmgr)
{
mgr = _mgr;
postmgr = _postmgr;
prodmgr = _prodmgr;
shopper = Cart.GetShopperId();
this.applicationDbContext = new ApplicationDbContext();
this.userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(this.applicationDbContext));
}
protected ApplicationDbContext applicationDbContext { get; set; }
protected UserManager<ApplicationUser> userManager { get; set; }
Which as far as I understand it points the identity code to use the right DbContext - I looked at doing this in reverse in my AccountController but can't fathom it out.
I basically just want to be able to use my own code that grabs my own data from within the Identity controllers to help pass extra data etc through to the views.
I might be wrong but most probably postmgr field is not initialized from constructor and that is why you have this error.
Explanation:
By default Asp will try to create controller instance by constructor without parameters. If Asp can't find constructor without parameters it will try to call constructor with parameters, but to make it possible you have to configure IoC in your app. As your controler has constructor without parameters it will be selected by Asp. So all 3 fields are empty.
But in properties SignInManager and UserManager you try to take value from field or from OwinContext. As field is empty your code will take value from OwinContext. OwinContext is quite complex and smart tool that create its context automatically based on configuration provided in Startup.Auth.cs file or any other file under App_Start folder.
I think I have figured it out - added the following to my NinjectControllerFactory :
ninjectKernel.Bind<IAuthenticationManager>().ToMethod(c => HttpContext.Current.GetOwinContext().Authentication); //.InRequestScope();
ninjectKernel.Bind<IUserStore<ApplicationUser>>().To<UserStore<ApplicationUser>>();
ninjectKernel.Bind<UserManager<ApplicationUser>>().ToSelf();
ninjectKernel.Bind<IRoleStore<IdentityRole, string>>().To<RoleStore<IdentityRole, string, IdentityUserRole>>();
ninjectKernel.Bind<RoleManager<IdentityRole>>().ToSelf();
And changed my constructor to :
public AccountController(PostageManager _postmgr)
{
postmgr = _postmgr;
}

How to properly MVC this task?

My task is to upload file.
class FileUploadController : Controller {
public ActionResult Index(HttpPostedFileBase postedFile) {
// When and how to validate it and return appropriate view and model data
// How to store the file in database and appropriately return view and model data
}
}
Validation requires to check if filename already exists(database access) and if file extension(database access) is supported.
So far I architected it like this:
class FileUploadController : Controller {
public ActionResult Index(HttpPostedFileBase postedFile) {
FileUploadModel model=new FileUploadModel();
model.UploadedFile = postedFile;
FileUploadService service = new FileUploadService();
bool valid = service.Validate(postedFile);
if (valid) {
FileUploadViewModel viewModel = service.Save(postedFile);
return View("some_view", viewModel);
}
else {
return View("some_view", service.ViewModel);
}
}
}
public class FileUploadModel
{
public HttpPostedFileBase UploadedFile { get; set; }
}
class FileUploadViewModel {
public ModelState ModelState;
public String Filename;
}
public class FileUploadService
{
private FileUploadViewModel viewModel = new FileUploadViewModel();
public FileUploadViewModel Save(FileUploadModel fileUploadModel)
{
// here i will just save it to the database
// and return viewModel with valid state
}
public bool Validate(FileUploadModel fileUploadModel)
{
// I do the filename, size, etc validation here together with database validation if the file exists and appropriately attach errors to viewModel.ModelState so views can render the error
}
}
As you can see my validate method populates viewModel.ModelState and my Save method returns new FileUploadViewModel. I really can't make up my mind how to design this so it can grow.
My questions are:
- If suddenly update action is added and my service serve update method, I will need to return different data as ViewModel and the validation would be different, should I create new ViewModel class and new Validation..?
- Does my validation occur at valid place?

ASP.NET MVC - Alternative to Role Provider?

I'm trying to avoid the use of the Role Provider and Membership Provider since its way too clumsy in my opinion, and therefore I'm trying to making my own "version" which is less clumsy and more manageable/flexible. Now is my question.. is there an alternative to the Role Provider which is decent? (I know that I can do custom Role provier, membership provider etc.)
By more manageable/flexible I mean that I'm limited to use the Roles static class and not implement directly into my service layer which interact with the database context, instead I'm bound to use the Roles static class which has its own database context etc, also the table names is awful..
Thanks in advance.
I'm in the same boat as you - I've always hated the RoleProviders. Yeah, they're great if you want to get things up and running for a small website, but they're not very realistic. The major downside I've always found is that they tie you directly to ASP.NET.
The way I went for a recent project was defining a couple of interfaces that are part of the service layer (NOTE: I simplified these quite a bit - but you could easily add to them):
public interface IAuthenticationService
{
bool Login(string username, string password);
void Logout(User user);
}
public interface IAuthorizationService
{
bool Authorize(User user, Roles requiredRoles);
}
Then your users could have a Roles enum:
public enum Roles
{
Accounting = 1,
Scheduling = 2,
Prescriptions = 4
// What ever else you need to define here.
// Notice all powers of 2 so we can OR them to combine role permissions.
}
public class User
{
bool IsAdministrator { get; set; }
Roles Permissions { get; set; }
}
For your IAuthenticationService, you could have a base implementation that does standard password checking and then you could have a FormsAuthenticationService that does a little bit more such as setting the cookie etc. For your AuthorizationService, you'd need something like this:
public class AuthorizationService : IAuthorizationService
{
public bool Authorize(User userSession, Roles requiredRoles)
{
if (userSession.IsAdministrator)
{
return true;
}
else
{
// Check if the roles enum has the specific role bit set.
return (requiredRoles & user.Roles) == requiredRoles;
}
}
}
On top of these base services, you could easily add services to reset passwords etc.
Since you're using MVC, you could do authorization at the action level using an ActionFilter:
public class RequirePermissionFilter : IAuthorizationFilter
{
private readonly IAuthorizationService authorizationService;
private readonly Roles permissions;
public RequirePermissionFilter(IAuthorizationService authorizationService, Roles requiredRoles)
{
this.authorizationService = authorizationService;
this.permissions = requiredRoles;
this.isAdministrator = isAdministrator;
}
private IAuthorizationService CreateAuthorizationService(HttpContextBase httpContext)
{
return this.authorizationService ?? new FormsAuthorizationService(httpContext);
}
public void OnAuthorization(AuthorizationContext filterContext)
{
var authSvc = this.CreateAuthorizationService(filterContext.HttpContext);
// Get the current user... you could store in session or the HttpContext if you want too. It would be set inside the FormsAuthenticationService.
var userSession = (User)filterContext.HttpContext.Session["CurrentUser"];
var success = authSvc.Authorize(userSession, this.permissions);
if (success)
{
// Since authorization is performed at the action level, the authorization code runs
// after the output caching module. In the worst case this could allow an authorized user
// to cause the page to be cached, then an unauthorized user would later be served the
// cached page. We work around this by telling proxies not to cache the sensitive page,
// then we hook our custom authorization code into the caching mechanism so that we have
// the final say on whether or not a page should be served from the cache.
var cache = filterContext.HttpContext.Response.Cache;
cache.SetProxyMaxAge(new TimeSpan(0));
cache.AddValidationCallback((HttpContext context, object data, ref HttpValidationStatus validationStatus) =>
{
validationStatus = this.OnCacheAuthorization(new HttpContextWrapper(context));
}, null);
}
else
{
this.HandleUnauthorizedRequest(filterContext);
}
}
private void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
// Ajax requests will return status code 500 because we don't want to return the result of the
// redirect to the login page.
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.Result = new HttpStatusCodeResult(500);
}
else
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
public HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext)
{
var authSvc = this.CreateAuthorizationService(httpContext);
var userSession = (User)httpContext.Session["CurrentUser"];
var success = authSvc.Authorize(userSession, this.permissions);
if (success)
{
return HttpValidationStatus.Valid;
}
else
{
return HttpValidationStatus.IgnoreThisRequest;
}
}
}
Which you can then decorate on your controller actions:
[RequirePermission(Roles.Accounting)]
public ViewResult Index()
{
// ...
}
The advantage of this approach is you can also use dependency injection and an IoC container to wire things up. Also, you can use it across multiple applications (not just your ASP.NET one). You would use your ORM to define the appropriate schema.
If you need more details around the FormsAuthorization/Authentication services or where to go from here, let me know.
EDIT: To add "security trimming", you could do it with an HtmlHelper. This probably needs a little more... but you get the idea.
public static bool SecurityTrim<TModel>(this HtmlHelper<TModel> source, Roles requiredRoles)
{
var authorizationService = new FormsAuthorizationService();
var user = (User)HttpContext.Current.Session["CurrentUser"];
return authorizationService.Authorize(user, requiredRoles);
}
And then inside your view (using Razor syntax here):
#if(Html.SecurityTrim(Roles.Accounting))
{
<span>Only for accounting</span>
}
EDIT: The UserSession would look something like this:
public class UserSession
{
public int UserId { get; set; }
public string UserName { get; set; }
public bool IsAdministrator { get; set; }
public Roles GetRoles()
{
// make the call to the database or whatever here.
// or just turn this into a property.
}
}
This way, we don't expose the password hash and all other details inside the session of the current user since they're really not needed for the user's session lifetime.
I have implemented a role provider based on #TheCloudlessSky post here. There are few things that I thought I can add and share what I have done.
First if you want to use the RequirepPermission class for your action filters as an attribute you need to implement ActionFilterAttribute class for RequirepPermission class.
Interface classes IAuthenticationService and IAuthorizationService
public interface IAuthenticationService
{
void SignIn(string userName, bool createPersistentCookie);
void SignOut();
}
public interface IAuthorizationService
{
bool Authorize(UserSession user, string[] requiredRoles);
}
FormsAuthenticationService class
/// <summary>
/// This class is for Form Authentication
/// </summary>
public class FormsAuthenticationService : IAuthenticationService
{
public void SignIn(string userName, bool createPersistentCookie)
{
if (String.IsNullOrEmpty(userName)) throw new ArgumentException(#"Value cannot be null or empty.", "userName");
FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
}
public void SignOut()
{
FormsAuthentication.SignOut();
}
}
UserSession calss
public class UserSession
{
public string UserName { get; set; }
public IEnumerable<string> UserRoles { get; set; }
}
Another point is FormsAuthorizationServiceclass and how we can assign a user to the httpContext.Session["CurrentUser"]. My Approach in this situation is to create a new instance of userSession class and directly assign the user from httpContext.User.Identity.Name to the userSession variable as you can see in FormsAuthorizationService class.
[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method, Inherited = false)]
public class RequirePermissionAttribute : ActionFilterAttribute, IAuthorizationFilter
{
#region Fields
private readonly IAuthorizationService _authorizationService;
private readonly string[] _permissions;
#endregion
#region Constructors
public RequirePermissionAttribute(string requiredRoles)
{
_permissions = requiredRoles.Trim().Split(',').ToArray();
_authorizationService = null;
}
#endregion
#region Methods
private IAuthorizationService CreateAuthorizationService(HttpContextBase httpContext)
{
return _authorizationService ?? new FormsAuthorizationService(httpContext);
}
public void OnAuthorization(AuthorizationContext filterContext)
{
var authSvc = CreateAuthorizationService(filterContext.HttpContext);
// Get the current user... you could store in session or the HttpContext if you want too. It would be set inside the FormsAuthenticationService.
if (filterContext.HttpContext.Session == null) return;
if (filterContext.HttpContext.Request == null) return;
var success = false;
if (filterContext.HttpContext.Session["__Roles"] != null)
{
var rolesSession = filterContext.HttpContext.Session["__Roles"];
var roles = rolesSession.ToString().Trim().Split(',').ToList();
var userSession = new UserSession
{
UserName = filterContext.HttpContext.User.Identity.Name,
UserRoles = roles
};
success = authSvc.Authorize(userSession, _permissions);
}
if (success)
{
// Since authorization is performed at the action level, the authorization code runs
// after the output caching module. In the worst case this could allow an authorized user
// to cause the page to be cached, then an unauthorized user would later be served the
// cached page. We work around this by telling proxies not to cache the sensitive page,
// then we hook our custom authorization code into the caching mechanism so that we have
// the final say on whether or not a page should be served from the cache.
var cache = filterContext.HttpContext.Response.Cache;
cache.SetProxyMaxAge(new TimeSpan(0));
cache.AddValidationCallback((HttpContext context, object data, ref HttpValidationStatus validationStatus) =>
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}, null);
}
else
{
HandleUnauthorizedRequest(filterContext);
}
}
private static void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
// Ajax requests will return status code 500 because we don't want to return the result of the
// redirect to the login page.
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.Result = new HttpStatusCodeResult(500);
}
else
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
private HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext)
{
var authSvc = CreateAuthorizationService(httpContext);
if (httpContext.Session != null)
{
var success = false;
if (httpContext.Session["__Roles"] != null)
{
var rolesSession = httpContext.Session["__Roles"];
var roles = rolesSession.ToString().Trim().Split(',').ToList();
var userSession = new UserSession
{
UserName = httpContext.User.Identity.Name,
UserRoles = roles
};
success = authSvc.Authorize(userSession, _permissions);
}
return success ? HttpValidationStatus.Valid : HttpValidationStatus.IgnoreThisRequest;
}
return 0;
}
#endregion
}
internal class FormsAuthorizationService : IAuthorizationService
{
private readonly HttpContextBase _httpContext;
public FormsAuthorizationService(HttpContextBase httpContext)
{
_httpContext = httpContext;
}
public bool Authorize(UserSession userSession, string[] requiredRoles)
{
return userSession.UserRoles.Any(role => requiredRoles.Any(item => item == role));
}
}
then in your controller after the user is authenticated you can get roles from the database and assign it to the roles session:
var roles = Repository.GetRolesByUserId(Id);
if (ControllerContext.HttpContext.Session != null)
ControllerContext.HttpContext.Session.Add("__Roles",roles);
FormsService.SignIn(collection.Name, true);
After the user is logged out of the system you can clear the session
FormsService.SignOut();
Session.Abandon();
return RedirectToAction("Index", "Account");
The caveat in this model is that, when the user is signed into the system, if a role is assigned to the user, authorization doesn't work unless he logs out and logs back in the system.
Another thing is that there is no need to have a separate class for roles, since we can get roles directly from database and set it into roles session in a controller.
After you are done with implementing all these codes one last step is to bind this attribute to your methods in your controller:
[RequirePermission("Admin,DM")]
public ActionResult Create()
{
return View();
}
If you use Castle Windsor Dependency Injection you can inject lists of RoleProviders that can be used to ascertain user rights from any source you choose to implement.
http://ivida.co.uk/2011/05/18/mvc-getting-user-roles-from-multiple-sources-register-and-resolve-arrays-of-dependencis-using-the-fluent-api/
You don't need to use a static class for roles. For instance, the SqlRoleProvider allows you to define the roles in a database.
Of course, if you want to retrieve roles from your own service layer, it's not that hard to create your own role provider - there really aren't that many methods to implement.
You can implement your own membership and role providers by overriding the appropriate interfaces.
If you want to start from scratch, typically these types of things are implemented as a custom http module which stores the users credentials either in the httpcontext or the session. Either way you'll probably want to set a cookie with some sort of authentication token.

Resources