How to share a common object in each page request of a ASP.net MVC 4 webapp? - asp.net

I come from "regular" asp.net so i'm a bit (totally) lost with MVC.
What I was doing with my own asp.net programmation pattern :
I have one custom class objet which represent the "page" and its properties (like mypage.loadJquery, mypage.isLogged, mypage.Title, custom cache logic, etc.)
This class is instanciate once on top of each ASHX page, I then manipulate a stringbuilder to produce HTML and spit it right at the browser at the end.
Having only one request on the ASHX page, i can use my page object instanciated at the top till the end when calling final response.write
Now i'm trying to go for MVC. I "kind of" understood the M/V/C model and the routing concept. I would like to keep my custom "page" object but I lost my page life cycle and I definitely don't know how to instanciate my page object ONCE in at the top of every call.
I need this instanciated ONCE shared object across every models, controllers, views, partial views, htmlhelper...
I realize MVC pattern might be confusing for me at this moment, bu how could I try to reproduce my need ?
(Very concrete exemple : On every request i need to check if the user is logged via his cookies. If it is I round trip the database to get user infos. Then I DO NEED THESE INFOS ON PRATICALLY EVERY model / controller / view of the app, but of course don't want to round back each time to security check and database querying, how can i have these info on the whole mvc cyle ?)

In my project I create interface IViewModel that contains all fields that I need in my layout/masterpage and set is as model of it so I can easily use them:
IViewModel.cs
public interface IViewModel
{
string Title { get; set; }
User User { get; set; }
}
Layout.cshtml
#model IViewModel
<html>
<head>
<title>#Model.Title</title>
</head>
<body>
#if (Model.User.IsAuthenticated) {
You are logged as #Model.User.Name
}
</body>
</html>
All my models implement that interface (or inherit from ViewModelBase that is default implementation of that class). Additionally I have custom action filter that check if returned ActionResult is (Partial)ViewResult and if Model of it implement my IViewModel interface and fill data in that interface.
public FillViewModelAttribute : ActionFilterAttribute
{
public override OnActionExecuted(ActionExecutedContext context)
{
var viewResult = context.Result as ViewResult;
if (viewResult != null && viewResult.Model is IViewModel)
{
var viewModel = (IViewModel)viewResult.Model;
// fill data
}
}
}

I created many projects like this. Basically, you can create a base controller class where all the other controllers inherit from it.
[Authorize]
public class BaseController : Controller
{
private Instructor _G_USER = null;
protected Instructor G_USER
{
get
{
if (_G_USER == null)
{
_G_USER = Your DB query here
ViewData["G_USER"] = _G_USER;
}
return _G_USER;
}
}
}
Then in your every child class, you can do
[Authorize]
public class YourController : BaseController
{
public ActionResult Index()
{
if(!G_USER.CAN_DO_THIS) throw new NoPermissionException();
return View();
}
}
To use the User in the view, create an extension method.
public static class ExtentionMethods
{
public static USER G_USER(this ViewPage page)
{
return (USER)page.ViewData["G_USER"];
}
}
Then use in the page like this
<%=this.G_USER().....%>

Related

Dynamic role providing in asp.net mvc (Roles are not fixed It is keep updating)

I am aware simple role provider in which if i need to restrict particular action i have to simply write Authorize(Roles = "Admin") or if i need to restrict particular part of view i nned to write #if(User.IsInRole("Admin")).
But my question is that what if my roles are not fixed and it is stored in database and my super admin can able to edit and delete them.
My requirement is that superadmin can add,update,delete roles and also create different users and maintain the roles of those users.
I have done lot of googling and found something as follows
[AttributeUsage (AttributeTargets.Method|AttributeTargets.Class,Inherited = true,AllowMultiple=true) ]
public class CustomRole : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase context)
{
Respository db = new Respository();
if (db.UserMasters.Where(x => x.user_name == context.User.Identity.Name).Count() > 0)
{
return true;
}
else { return false; }
}
}
Here i can use this code to authorize action method as follows
[CustomRole]
public ActionResult Details(int id = 0)
{
Employee employee = db.Employees.Find(id);
if (employee == null)
{
return HttpNotFound();
}
return View(employee);
}
Here my this action method is protected but what if i want to protect some part of view by this custom method. How to use this functionality to achieve functionality as User.IsInRole("Admin")?
your requirement will get in 3 steps
1- Create all default roles, store it in database.i.e- roleid,rolename
2- When creating new user map userid with roleid.
3- also make one table for all permission which you have to give.
4- make seperate ui for admin to change the roles of each user.
database will be like below image.
and ui will be like this.
try this yousrelf..
Fully answering your question might be out of scope for StackOverflow, since it would basically require writing most of an application for you, but here's the general idea.
Write a helper class similar to this:
public class ModuleHelper
{
public static bool UserCanAccessModule(string moduleIdentifier)
{
bool canAccess = false;
/*
Call into your service with current User.Name and module identifier (integer, GUID, whatever).
Return result of whether user has the required role for the specified module
*/
try
{
canAccess = service.CanUserAccessModule(User.Identity.Name, moduleIdentifier);
}
catch
{
// catching all exceptions, since this is a UI helper
}
return canAccess;
}
// etcetera...
}
I'd suggest wrapping it in the root namespace of your application; otherwise, add a reference to this class's namespace in the system.web.webPages.razor section of the web.config in the Views folder. Then, you can do something like:
<div class="col-sm-3 col-md-2 sidebar">
#if (ModuleHelper.UserCanAccessModule("moduleXYZ"))
{
#Html.Action("moduleXYZ")
}
</div>
This obviously assumes a lot, but the idea isn't new or all that complicated in practice. The logic of the service is relatively simple:
Look up the user
Look up the "action" or "module"
Look for intersection (if any) between the roles assigned to each.
No intersection means user doesn't have the required role.
Tieson T. has a great answer to your question already, so what I'll provide here is an alternative method if you wanted to keep all of your authorization steps all in controllers.
Consider separating the different aspects (or restricted parts) of your main view into a partial view (or views) that perform the restricted functionality. Then, instead of using: #Html.RenderPartial("ViewName", Model) you can set up your partials to be returned from controller actions decorated with the ChildActionOnly Attribute by using the RenderAction Html Helper.
For example:
<div class="col-sm-3 col-md-2 sidebar">
#Html.RenderAction("RestrictedContent")
</div>
Then in your controller class
public class RestrictedController : Controller {
public RestrictedController() : base() {
}
[ChildActionOnly()]
[CustomRole()]
public ActionResult RestrictedContent() {
return PartialView("RestrictedPartial");
} // end action RestrictedContent
} // end class
The only consideration with this approach will be in your custom attribute to interrogate the the IsChildAction property to avoid rendering a redirect or whatever your attribute does in the case the user is not authorized since you'll probably want to just not render anything.
For Example (in your custom attribute class):
public override void OnAuthorization(AuthorizationContext filterContext) {
if(filterContext.IsChildAction) {
filterContext.Result = new EmptyResult(); // return an empty result instead of performing a redirect.
} else {
base.OnAuthorization(filterContext); // continue with custom authorization if it is not a child action
} // end if/else
} // end method OnAuthorization
Phil Haack has an article describing the usage of the RenderAction method here: http://haacked.com/archive/2009/11/18/aspnetmvc2-render-action.aspx/
Also, see here for an interesting discussion on the differences between Action and RenderAction. The difference between Html.Action and Html.RenderAction

How to Link to OData Collection in Razor using ASP.NET MVC Web API OData

I have an ASP.NET MVC 4 app that i'm incorporating an OData API into. This is running the 2012.2 stuff with the larger OData support.
I did not use a separate area for this...that might have been a mistake but my app is small and area seemed overkill.
I've got my controllers setup correctly and an example path to my Segments collection (segments is a type in my domain) is "/odata/Segments". This loads as expected and is working.
On my homepage i'm trying to add a link to this resource using Razor's Html.ActionLink (or RouteLink) but it seems the OData controllers layout doesn't quite work with those methods because the controllers are prefixed with "odata" when registered in WebAPIConfig:
config.Routes.MapODataRoute("OData Route", "odata", model );
I can trick the method to construct the correct url by pretending there's an odata controller when there certainly isn't one (as far as i know) with something like this:
#Html.RouteLink("Segments", "Segments", "odata")
but that seems like a hack.
I don't quite understand the ASP.NET routing plumbing well enough to understand how that prefix passed to MapODataRoute is being incorporated into the MVC chain so that i can use the "right" razor method the "right" way.
just for kicks, here's my SegmentsController:
public class SegmentsController : EntitySetController<Segment, long>
{
private MarketerDB db = new MarketerDB();
// GET api/segments
override public IQueryable<Segment> Get()
{
return db.Segments.AsQueryable();
}
protected override Segment GetEntityByKey(long key)
{
return db.Segments.Find(key);
}
public IQueryable<Affiliate> GetAffiliates([FromODataUri] long key)
{
return this.GetEntityByKey(key).Affiliates.AsQueryable();
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
We have an ODataLink method on System.Web.Http.UrlHelper but we forgot to add one to the MVC System.Web.Mvc.UrlHelper. Till we add it, you can use this extension method,
namespace System.Web.Mvc
{
public static class UrlHelperExtensions
{
private static IODataPathHandler _pathHandler = new DefaultODataPathHandler();
public static string ODataUrl(this UrlHelper urlHelper, string routeName, params ODataPathSegment[] segments)
{
string odataPath = _pathHandler.Link(new ODataPath(segments));
return urlHelper.HttpRouteUrl(
routeName,
new RouteValueDictionary() { { ODataRouteConstants.ODataPath, odataPath } });
}
}
}
and call it from your razor views by doing something like (assuming there is an entityset customers and you want to put the navigation link to orders on customers(42)),
#Url.ODataUrl("odata", new EntitySetPathSegment("customers"), new KeyValuePathSegment("42"), new NavigationPathSegment("orders"))
Make sure you have an #using System.Web.Http.OData.Routing directive in your razor view.

Access model from shared partial view independent of controller

I'm creating a simple webpage in asp.net mvc 3.
I have a sidebar that loads random citations, and the sidebar is on every page so it's a part of the layout, independent of the controller.
What is the correct way to access a datamodel from that view? Do I have to pass data from each controller?
My partial view file looks something like:
#model MvcApplication1.Models.CitationModel
#Model.Citation
But this results in a null reference.
The model is something like
public class CitationModel
{
public string Citation{ get { return "Test"; } }
}
I would do this with a child action. This way you can keep the view strongly typed (no viewbag or viewdata), without having to put it in a "master" viewmodel that gets sent to your layout:
<div id="sidebar">
#Html.Action("RandomCitations", "Citations")
</div>
In CitationsController:
[ChildActionOnly]
public PartialViewResult RandomCitations()
{
var model = new CitationModel();
// populate model
return PartialView(model);
}
Your view will stay the same, and will be injected into the sidebar div for every layout.
There are plenty of scenarios there.
For now for that cases I put model to view bag, and then getting it from viewbag on view.
I'd use a base controller class like this:
public class ApplicationController : Controller
{
public ApplicationController()
{
Citation c = getYourCitation();
ViewBag.Citation = c;
}
}
Get all your controllers to inherit from Application controller
public class HomeController : ApplicationController
{
//Controller code
}
Each view (including _Layout) will then be able to access the ViewBag
in _Layout.cshtml do this:
#Html.Partial("_CitationPartial", (Your.MidTier.Models.Citation)ViewBag.Citation)

Multiple controllers, one view, and one model ASP.NET MVC 3

I want to have one model & view that is served by multiple controllers in my ASP.NET MVC 3 app.
I'm implementing a system that interacts with the users' online calendar and I support Exchange, Google, Hotmail, Yahoo, Apple, ect... Each of these has wildly different implementations of calendar APIs, but I can abstract that away with my own model. I'm thinking that by implementing the polymorphism at the controller level I will be able to deal cleanly with the different APIs and authentication issues.
I have a nice clean model and view and I've implemented two controllers so far that prove I can read/query/write/update to both Exchange and Google: ExchangeController.cs and GoogleController.cs.
I have /Views/Calendar which contains my view code. I also have /Models/CalendarModel.cs that includes my model.
I want the test for which calendar system the user is using to happen in my ControllerFactory. I've implemented it like this:
public class CustomControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == typeof(CalendarController))
{
if(MvcApplication.IsExchange) // hack for now
return new ExchangeController();
else
return new GoogleController();
}
return base.GetControllerInstance(requestContext, controllerType);
}
}
and in my Application_Start:
ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());
This works. If I got to http://.../Calendar this factory code works and the correct controller is created!
This worked beautifully and I did it without really understanding what I was doing. Now i think I got it but I want to make sure I'm not missing something. I really spent time searching for something like this and didn't find anything.
One thing that concerns me is that I figured I'd be able to have an inheritance relationship between CalendarController and ExchangeController/GoogleController like this:
public class ExchangeController : CalendarController
{
But if I do that I get:
The current request for action 'Index' on controller type 'GoogleController' is ambiguous between the following action methods:
System.Web.Mvc.ViewResult Index(System.DateTime, System.DateTime) on type Controllers.GoogleController
System.Web.Mvc.ActionResult Index() on type Controllers.CalendarController
Which bums me out because I wanted to put some common functionality on the base and now I guess I'll have to use another way.
Is this the right way to do have multiple controllers for one view/model? What else am I going to have to consider?
EDIT: More details on my impl
Based on the responses below (thanks!) I think I need to show some more code to make sure you guys see what I'm trying to do. My model is really just a data model. It starts with this:
/// <summary>
/// Represents a user's calendar across a date range.
/// </summary>
public class Calendar
{
private List<Appointment> appointments = null;
/// <summary>
/// Date of the start of the calendar.
/// </summary>
public DateTime StartDate { get; set; }
/// <summary>
/// Date of the end of the calendar
/// </summary>
public DateTime EndDate { get; set; }
/// <summary>
/// List of all appointments on the calendar
/// </summary>
public List<Appointment> Appointments
{
get
{
if (appointments == null)
appointments = new List<Appointment>();
return appointments;
}
set { }
}
}
Then my controller has the following methods:
public class ExchangeController : Controller
{
//
// GET: /Exchange/
public ViewResult Index(DateTime startDate, DateTime endDate)
{
// Exchange specific gunk. The MvcApplication._service thing is a temporary hack
CalendarFolder calendar = (CalendarFolder)Folder.Bind(MvcApplication._service, WellKnownFolderName.Calendar);
Models.Calendar cal = new Models.Calendar();
cal.StartDate = startDate;
cal.EndDate = endDate;
// Copy the data from the exchange object to the model
foreach (Microsoft.Exchange.WebServices.Data.Appointment exAppt in findResults.Items)
{
Microsoft.Exchange.WebServices.Data.Appointment a = Microsoft.Exchange.WebServices.Data.Appointment.Bind(MvcApplication._service, exAppt.Id);
Models.Appointment appt = new Models.Appointment();
appt.End = a.End;
appt.Id = a.Id.ToString();
...
}
return View(cal);
}
//
// GET: /Exchange/Details/5
public ViewResult Details(string id)
{
...
Models.Appointment appt = new Models.Appointment();
...
return View(appt);
}
//
// GET: /Exchange/Edit/5
public ActionResult Edit(string id)
{
return Details(id);
}
//
// POST: /Exchange/Edit/5
[HttpPost]
public ActionResult Edit(MileLogr.Models.Appointment appointment)
{
if (ModelState.IsValid)
{
Microsoft.Exchange.WebServices.Data.Appointment a = Microsoft.Exchange.WebServices.Data.Appointment.Bind(MvcApplication._service, new ItemId(appointment.Id));
// copy stuff from the model (appointment)
// to the service (a)
a.Subject = appointment.Subject
...
a.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone);
return RedirectToAction("Index");
}
return View(appointment);
}
//
// GET: /Exchange/Delete/5
public ActionResult Delete(string id)
{
return Details(id);
}
//
// POST: /Exchange/Delete/5
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(string id)
{
Microsoft.Exchange.WebServices.Data.Appointment a = Microsoft.Exchange.WebServices.Data.Appointment.Bind(MvcApplication._service, new ItemId(id));
a.Delete(DeleteMode.MoveToDeletedItems);
return RedirectToAction("Index");
}
So it's basically the typical CRUD stuff. I've provided the sample from the ExchangeCalendar.cs version. The GoogleCalendar.cs is obviously similar in implementation.
My model (Calendar) and the related classes (e.g. Appointment) are what get passed from controller to view. I don't want my view to see details of what underlying online service is being used. I do not understand how implementing the Calendar class with an interface (or abstract base class) will give me the polymorphism I am looking for.
SOMEWHERE I have to pick which implementation to use based on the user.
I can either do this:
In my model. I don't want to do this because then my model gets all crufty with service specific code.
In the controller. E.g. start each controller method with something that redirects to the right implementation
Below the controller. E.g. as I'm suggesting above with a new controller factory.
The responses below mention "service layer". I think this is, perhaps, where I'm off the rails. If you look at the way MVC is done normally with a database, the dbContext represents the "service layer", right? So maybe what you guys are suggesting is a 4th place where I can do the indirection? For example Edit above would go something like this:
private CalendarService svc = new CalendarService( e.g. Exchange or Google );
//
// POST: /Calendar/Edit/5
[HttpPost]
public ActionResult Edit(MileLogr.Models.Appointment appointment)
{
if (ModelState.IsValid)
{
svc.Update(appointment);
return RedirectToAction("Index");
}
return View(appointment);
}
Is this the right way to do it?
Sorry this has become so long-winded, but it's the only way I know how to get enough context across...
END EDIT
I wouldn't do it this way. As Jonas points out, controllers should be very simple and are intended to coordinate various "services" which are used to respond to the request. Are the flows of requests really all that different from calendar to calendar? Or is the data calls needed to grab that data different.
One way to do this would be to factor your calendars behind a common calendar interface (or abstract base class), and then accept the calendar into the controller via a constructor parameter.
public interface ICalendar {
// All your calendar methods
}
public abstract class Calendar {
}
public class GoogleCalendar : Calendar {}
public class ExchangeCalendar : Calendar {}
Then within your CalendarController,
public class CalendarController {
public CalendarController(ICalendar calendar) {}
}
This won't work by default, unless you register a dependency resolver. One quick way to do that is to use NuGet to install a package that sets one up. For example:
Install-Package Ninject.Mvc3
I think this would be a better architecture. But suppose you disagree, let me answer your original question.
The reason you get the ambiguous exception is you have two public Index methods that are not distinguished by an attribute that indicates one should respond to GETs and one to POSTs. All public methods of a controller are action methods.
If the CalendarController isn't meant to be instantiated directly (i.e. it'll always be inherited), then I would make the Index method on that class protected virtual and then override it in the derived class.
If the CalendarController is meant to be instantiated on its own, and the other derived classes are merely "flavors" of it, then you need to make the Index method public virtual and then have each of the derived classes override the Index method. If they don't override it, they're adding another Index method (C# rules, not ours) and you need to distinguish them for MVC's sake.
I think you're on a dangerous path here. A controller should generally be as simple as possible, and only contain the "glue" between e.g. your service layer and the models/views. By moving your general calendar abstractions and vendor specific implementations out of the controllers, you get rid of the coupling between your routes and the calendar implementation.
Edit: I would implement the polymorphism in the service layer instead, and have a factory class in the service layer check your user database for the current user's vendor and instantiate the corresponding implementation of a CalendarService class. This should eliminate the need for checking the calendar vendor in the controller, keeping it simple.
What I mean by coupling to the routes is that your custom URLs is what is currently causing you problems AFAICT. By going with a single controller and moving the complexity to the service layer, you can probably just use the default routes of MVC.
As the other answers suggest, you really should refactor your code so as to not require the multiple controllers in the first place.
However, you can still have your controllers inherit from a base class controller - you simply need to make sure that when you register the routes in the Global.asax.cs, you use the overload that specifies which namespace to find the controllers and action methods for a given route
e.g.
routes.MapRoute(null, "{controller}/{action}", new[] { "Namespace.Of.Controllers.To.USe" });

Best way to do global viewdata in an area of my ASP.NET MVC site?

I have an several controllers where I want every ActionResult to return the same viewdata. In this case, I know I will always need basic product and employee information.
Right now I've been doing something like this:
public ActionResult ProductBacklog(int id) {
PopulateGlobalData(id);
// do some other things
return View(StrongViewModel);
}
Where PopulateGlobalData() is defined as:
public void PopulateGlobalData(int id) {
ViewData["employeeName"] = employeeRepo.Find(Thread.CurrentPrincipal.Identity.Name).First().FullName;
ViewData["productName"] = productRepo.Find(id).First().Name;
}
This is just pseudo-code so forgive any obvious errors, is there a better way to be doing this? I thought of having my controller inherit a class that pretty much does the same thing you see here, but I didn't see any great advantages to that. It feels like what I'm doing is wrong and unmaintable, what's the best way to go about this?
You could write a custom action filter attribute which will fetch this data and store it in the view model on each action/controller decorated with this attribute.
public class GlobalDataInjectorAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
string id = filterContext.HttpContext.Request["id"];
// TODO: use the id and fetch data
filterContext.Controller.ViewData["employeeName"] = employeeName;
filterContext.Controller.ViewData["productName"] = productName;
base.OnActionExecuted(filterContext);
}
}
Of course it would much cleaner to use a base view model and strongly typed views:
public class GlobalDataInjectorAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
string id = filterContext.HttpContext.Request["id"];
// TODO: use the id and fetch data
var model = filterContext.Controller.ViewData.Model as BaseViewModel;
if (model != null)
{
model.EmployeeName = employeeName;
model.ProductName = productName;
}
base.OnActionExecuted(filterContext);
}
}
Now all that's left is to is to decorate your base controller with this attribute:
[GlobalDataInjector]
public abstract class BaseController: Controller
{ }
There's another more interesting solution which I personally prefer and which involves child actions. Here you define a controller which handles the retrieval of this information:
public class GlobalDataController: Index
{
private readonly IEmployeesRepository _employeesRepository;
private readonly IProductsRepository _productsRepository;
public GlobalDataController(
IEmployeesRepository employeesRepository,
IProductsRepository productsRepository
)
{
// usual constructor DI stuff
_employeesRepository = employeesRepository;
_productsRepository = productsRepository;
}
[ChildActionOnly]
public ActionResult Index(int id)
{
var model = new MyViewModel
{
EmployeeName = _employeesRepository.Find(Thread.CurrentPrincipal.Identity.Name).First().FullName,
ProductName = _productsRepository.Find(id).First().Name;
};
return View(model);
}
}
And now all that's left is to include this wherever needed (probably the master page if global):
<%= Html.Action("Index", "GlobalData", new { id = Request["id"] }) %>
or if the id is part of the routes:
<%= Html.Action("Index", "GlobalData",
new { id = ViewContext.RouteData.GetRequiredString("id") }) %>
I thought of having my controller inherit a class that pretty much does the same thing you see here, but I didn't see any great advantages to that.
This is the way to go, in my opinion. You'd create a base Controller class that would provide this functionality. If you are familiar with the ASP.NET WebForms model then this is similar to creating a custom base Page class.
As to the advantages of putting it in a base class, the main advantages are readability, maintainability and reusability. If you copy and paste the above method into each controller that needs it, you are going to have a more difficult time if, down the road, you need to add new information to the ViewData collection.
In short, anytime you catch yourself copying and pasting code among classes or views in your application you should stop and think about how to put such logic in a single place. For more, read up on DRY - Don't Repeat Yourself.

Resources