Let say I have an object that is similar for each user that try to access to my website.
A sort of Session Scope object, which should be visible on every View/Model/Controller inside my whole "application".
I'd like to create it when I call a page and populate it through data coming from my own database.
Than, on View (as example) calling myObject.Title.
On WebForms I'm doing this extending a class of the UserControl, such as:
public class iUserControl : System.Web.UI.UserControl
{
protected MyCurrentPage myCurrentPage;
public iUserControl()
{
}
protected override void OnLoad(EventArgs e)
{
myCurrentPage = new MyCurrentPageWrapper();
}
}
than, for each UserControl, somethings like this:
public partial class context_pippo_MyOwnUserControl : iUserControl
on MVC I can't see any extension for each controls, so how can I could achieve this kind of process? I'd like to get rid about storing elements on Session.
If I understand question correctly, I think I've done something similar in one project.
I had something like this:
public interface IControllerBaseService
{
IUserService UserService {get;set;}
ShoppingMode ShoppingMode {get;set;}
...
}
public abstract class ControllerBase : Controller, IControllerBaseService
{
public IUserService UserService {get;set;} // this is injected by IoC
public ShoppingMode ShoppingMode
{
get
{
return UserService.CurrentShoppingMode; // this uses injected instance to get value
}
...
}
As long, as I user IoC container to create controller instances, UserService property is injected by container.
You can access now your interface from view like this:
(IControllerBaseService)ViewContext.Controller
To have a shortcuts for most common used properties in IControllerBaseService, I had several extension methods, something like this:
public static ShoppingMode CurrentShoppingMode(this HtmlHelper helper)
{
return ((IContollerBaseService)helper.ViewContext.Controller).ShoppingMode;
}
So in view it looked like #Html.CurrentShoppingMode()
Related
For Asp.net mvc core app:
In one of my dbcontext tables I have records with HelpInfo (id as primary key, and string for helpInfo) that I want to use as html title attributes in my razor views, such as:
<div title='#I(12)'></div>
Where 12 is an example id of a HelpInfo record and #I should be a global accessible class method that fetches the corresponding helpInfo string.
I tried to implement King Kings approach such as:
The CustomView:
public abstract class CustomView<TModel> : RazorPage<TModel>
{
protected IInfo Info => Context.RequestServices.GetRequiredService<IInfo>();
public Task<string> I(int code)
{
return Info.GetAsync(code);
}
}
The Interface:
public interface IInfo
{
Task<string> GetAsync(int code);
}
The Service:
public class Info : IInfo
{
private readonly ApplicationDbContext context;
public Info(ApplicationDbContext context)
{
this.context = context;
}
public async Task<string> GetAsync(int code)
{
var myRecord = await context.MyRecords.FindAsync(code);
if (myRecord != null)
{
return myRecord.Info;
}
return null;
}
}
In _ViewImports.cshtml I added #inherits CustomView<TModel>
In the view I used <div title='#(await I(12))'>Test</div>
When I load the view I get
No service for type '...Models.IInfo' has been registered
Any help to pin down the problem would be appreciated.
As I understand looks like you want something like #Html or #Url which are supported in the default Page and RazorPage. That's just a custom property exposed by the base page. So in your case, you need a custom view (for mvc) or a custom page (for razor pages). Here is an example of a custom view used for MVC:
public abstract class CustomView<TModel> : RazorPage<TModel>
{
//custom members can be declared in here
}
Based on your desired usage of I, it must be a method. So it can be a method exposed by some service injected in your base page. Suppose that interface is like this:
public interface IInfo {
string Get(int code);
}
Now your custom page can implement the I method like this:
public abstract class CustomView<TModel> : RazorPage<TModel>
{
//NOTE: the custom page class does not support constructor injection
//So we need to get the injected services via the Context like this.
protected IInfo Info => Context.RequestServices.GetRequiredService<IInfo>();
public string I(int code){
return Info.Get(code);
}
}
To use your custom view, you need to use the directive #inherits in your view or better in the _ViewImports.cshtml so that you don't have to repeat that #inherits everywhere, like this:
#inherits CustomView<TModel>
Sometimes the base view is not applied (so the base members are not available) until you rebuild your project.
Now you can use the I in your views as what you desire, like this:
<div title="#I(12)"></div>
Note the I method returns a string in my example, you can also make it return an IHtmlContent (e.g: HtmlString) or whatever type you need for your requirement.
NOTE:
Technically you can use any services (including ones querying for data from database), but in such cases please ensure that the querying is as fast as possible and use async to have the best performance & avoid thread starvation. Here is an example of the IInfo service that queries from a database:
public interface IInfo {
Task<string> GetAsync(int code);
}
public class Info : IInfo {
//inject whatever your Info needs
//here we just need some DbContext (used in EFCore)
public Info(InfoDbContext dbContext){
_infoDbContext = dbContext;
}
readonly InfoDbContext _infoDbContext;
public async Task<string> GetAsync(int code){
var info = await _infoDbContext....
return info;
}
}
The I method then should be async as well:
public Task<string> I(int code){
return Info.GetAsync(code);
}
I hope that you know how to register a DbContext to use in your code (that's part of EFCore so you may have to learn more about that first). Now to use the async method, you call it in your view like this:
<div title="#(await I(12))"></div>
Again, I would try to avoid querying the db in such a helper like that. As I said, the helper's method can be called multiple times right in one same view so usually we have fast methods or use caching for the info it need. This is related to another feature called caching which has more to be put in one short answer, you can learn more about that.
Solution:
To my Startup.cs I added:
services.AddScoped<IInfo, Info>();
services.AddTransient<IInfo, Info>(); also does work.
I have a base controller and before every page load I want to get the current user. I originally had a constructor in my BaseController that looked like this
public BaseController(ISystemUserCommand command)
{
_systemUserCommand = command
}
The problem with this then is that every controller that inherits from the BaseController would have to contain the ISystemUserCommand in its constructor, which I don't think would be good.
Instead I tried to create just an instance of the service class (shown below - it's the commented line under var sid...) but I need to pass in user service. How would I pass in the user service here or is this a bad way of doing it?
public abstract class BaseController : Controller
{
public SystemUserViewModel CurrentUser { get; set; }
private readonly ISystemUserCommand _systemUserCommand;
public SystemUserViewModel GetCurrentUser()
{
if (HttpContext == null || HttpContext.User == null) return null;
if (CurrentUser != null) return CurrentUser;
var sid = System.Web.HttpContext.Current.Request.LogonUserIdentity.User.ToString();
//var command = new SystemUserCommand();
CurrentUser = _systemUserCommand.GetUser(sid);
return CurrentUser;
}
public void SetUserInformation(SystemUserViewModel currentUser)
{
ViewBag.UserId = currentUser.SystemUserId;
ViewBag.FullName = string.Format("{0} {1}", currentUser.FirstName, currentUser.LastName);
ViewBag.FirstName = currentUser.FirstName;
ViewBag.LastName = currentUser.LastName;
ViewBag.CurrentUser = currentUser;
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var currentUser = GetCurrentUser();
if (currentUser != null)
{
if (currentUser.IsActive)
{
SetUserInformation(currentUser);
}
else
filterContext.Result = RedirectToAction("denied", "unauthorized");
}
else
filterContext.Result = RedirectToAction("denied", "unauthorized");
base.OnActionExecuting(filterContext);
}
}
public class SystemUserCommand : ISystemUserCommand
{
private readonly ISystemUserBusiness _systemUserBusiness;
public SystemUserCommand(ISystemUserBusiness systemUserBusiness)
{
_systemUserBusiness = systemUserBusiness;
}
...
}
You could use property injection instead of constructor injection, via the base class, eg using unity:
public abstract class BaseController : Controller
{
[Dependency]
public ISystemUserCommand SystemUserCommand { get; set; }
}
This would mean the interface reference is only on the base class.
See here for the full examples.
EDIT, Autofac example:
You don't need property attributes on the dependency,
public abstract class BaseController : Controller
{
public ISystemUserCommand SystemUserCommand { get; set; }
}
Just to register the properites to auto resolve on the autofac builder:
builder.RegisterControllers(typeof(MvcApplication).Assembly).Where(t => t.IsAssignableFrom(typeof(BaseController))).PropertiesAutowired();
See autofac property injection here.
First of all, it does not seem a good idea to have OnActionExecuting override in the controller. You can use filters, that are specially designed for this purpose. And it seems that is the main reason you created the BaseController at all.
Regarding the problem with injecting the system command in all the required service, I would do so, but without inheriting from a base class, since I generally prefer aggregation to inheritance. That would mean that each controller that needs to work with the service will get it.
Another option that I have used few times to abstract some operations is to create a UserSerivce that will provide the required operations to the controllers. It will have ISystemUserCommand and HttpContext injected inside so that all of your controllers won't have to do the job. You can either use HttpContext.Current as static or abstract it away if you need testability.
Moreover I would not recommend property injection since it is more obscure than constructor injection that should be preferred if possible.
You can read more about filters here. Unfortunately if you use filters it's not that easy to inject in filters themselves and mostly done with property injection or ServiceLocator pattern (which is not good usually). It's possible to do better with some amount of voodoo though. I think that SimpleInjector has a lot of examples and tutorials on how to apply DI to filters in MVC, maybe they even have a nuget package now to ahieve that.
I have a page like so -
public partial class ProductDetailMixed : SessionPage, IProductDetailMixedView
{
public IProductService ProductService { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
Controller = new ProductDetailMixedController(this, ProductService);
OnLoadPage();
}
public event LoadPageEvent OnLoadPage;
public IProductDetailMixedController Controller { get; set; }
}
I have property injection so the instance of IProductService will be injected into the page, however what I would really like is to just create the controller like so -
Controller = new ProductDetailMixedController(this);
And then the Constructor of ProductDetailMixedController will just inject all the remaining parameters.. but how do I do this??
I believe this is what you're trying to accomplish? In the referenced example, your presenter / controller factory would also need a parameter for your service interface. Once you have something like that in place, you could remove the property injection for your service from your view (unless you utilize it within your view, of course).
Since my #html.render action crashes my dev and prod servers i have to use partials(crap).
I tried creating public partial controller{} class so i can set needed data for all my views but i am having no luck (everything breaks).
I am coming from LAMP cakePHP background and really need simplicity.
I need to know how to create a partial base controller(that doesnt override the regular base controller) and how to access multiple models from the class.
Thank you!
public class BaseController: Controller
{
public override OnActionExecuting(...) { ... }
public override OnActionExecuted(... context)
{
if (context.Result is ViewResult)
((ViewResult)context.Result).ViewData["mycommondata"] = data;
}
...
}
public class MyController1: BaseController
{
}
I.e. just derive from your new base controller class.
However I'd suggest you to ask here why your RenderPartial "crashes" - since it can be a better way for you, and it obviously shouldn't crash.
better way to create base controller
public class Controller : System.Web.Mvc.Controller
{
public shipsEntities db = new shipsEntities();
public Controller()
{
ViewData["ships"] = db.ships.ToList();
}
}
that way the rest of controllers follow regular convention
public class MyController : Controller
When a user log in into my application i want to show his name throughout the whole application. I am using the asp.net MVC framework. But what i don't want is that is have to put in every controller something like:
ViewData["User"] = Session["User"];
This because you may not repeat yourself. (I believe this is the DRY [Don't Repeat Yourself] principle of OO programming.)
The ViewData["User"] is on my masterpage. So my question is, what is a neat way to handle my ViewData["User"] on one place?
You can do this fairly easily in either a controller base-class, or an action-filter that is applied to the controllers/actions. In either case, you get the chance to touch the request before (or after) the action does - so you can add this functionality there.
For example:
public class UserInfoAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(
ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
filterContext.Controller.ViewData["user"] = "Foo";
}
}
...
[HandleError, UserInfo]
public class HomeController : Controller
{...}
(can also be used at the action (method) level)
or with a common base-class:
public abstract class ControllerBase : Controller
{
protected override void OnActionExecuting(
ActionExecutingContext filterContext)
{
ViewData["user"] = "Bar";
base.OnActionExecuting(filterContext);
}
}
[HandleError]
public class HomeController : ControllerBase
{...}
It's been a year, but I've just stumbled across this question and I believe there's a better answer.
Jimmy Bogard describes the solution described in the accepted answer as an anti-pattern and offers a better solution involving RenderAction: http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/06/18/the-filter-viewdata-anti-pattern.aspx
Another method for providing persistent model data through out your entire application is by overriding the DefaultFactoryController with your custom one. In your CustomerFactoryController, you would hydrate the ViewBag with the model you are wanting to persist.
Create a base class for your models with UserName property:
public abstract class ModelBase
{
public string UserName { get; set; }
}
Create a base class for you controllers and override it's OnActionExecuted method. Within it check if model is derrived from BaseModel and if so, set it's UserName property.
public class ControllerBase : Controller
{
protected override void OnActionExecuted(
ActionExecutedContext filterContext)
{
var modelBase = ViewData.Model as ModelBase;
if (modelBase != null)
{
modelBase.UserName = "foo";
}
base.OnActionExecuted(filterContext);
}
}
Then you will be able to display user's UserName in the view like this:
<%= Html.Encode(Model.UserName) %>
See also:
ASP.NET MVC Best Practices, Tips and Tricks