System.NullReferenceException creating viewModel - c#

So, I'm trying to find the Umbraco node (as iPublishedContent), and pass it to the viewModel (as Ш've hijacked a route). So i put this in my controller:
private AddCouponCodesViewModel viewModel;
public AddCouponCodesController(){
//Get iPublished content
IPublishedContent content = Umbraco.TypedContent(1225);
//Pass to viewModel
viewModel = new AddCouponCodesViewModel(content);
RouteData.DataTokens["umbraco"] = content;
}
public ActionResult Index()
{
//return view etc
}
But I'm getting
Exception Details: System.NullReferenceException:
Object reference not set to an instance of an object.
here:
Source Error(AddCouponCodesViewModel.cs):
Line 20:
Line 21: }
Line 22: public AddCouponCodesViewModel(IPublishedContent content)
Line 23: : base(content)
Line 24: {
AddCouponCodeRenderModel.cs:
public class AddCouponCodesViewModel : RenderModel
{
public string test { get; set; }
public List<string> tables { get; set; }
public List<string> errors { get; set; }
public AddCouponCodesViewModel(IPublishedContent content, CultureInfo culture) : base(content, culture)
{
}
public AddCouponCodesViewModel(IPublishedContent content)
: base(content)
{
}
And this is the Global.asax
public class Global : UmbracoApplication
{
protected override void OnApplicationStarted(object sender, EventArgs e)
{
base.OnApplicationStarted(sender, e);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//AreaRegistration.RegisterAllAreas();
//WebApiConfig.Register(GlobalConfiguration.Configuration);
//FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
//RouteConfig.RegisterRoutes(RouteTable.Routes);
base.OnApplicationStarting(sender, e);
RouteTable.Routes.MapRoute(
"AddCouponCodes", // Route name
"Admin/{controller}/{action}/{id}", // URL with parameters
new { controller = "AddCouponCodes", action = "Index", id = "" } // Parameter defaults
);
}
}
The content is published (I've checked and double checked), and the node ID is correct.
What I'm basically trying to do here, is to get the route example.com/Admin/{controller}/{action}/{parameter}
To be routed, but having problems connecting it with the umbracoNode (And class RenderModel requires a iPublishContent object as a parameter, but I'm in no luck when trying to pass it anything)
Could someone please help me here, been stuck way too many hours on this :-(

To clarify, if you are hijacking a route, it means that you are overriding the way Umbraco passes it's RenderModel to one of it's published pages. You can either do this globally by overriding the main RenderMvcController, or you can override on a DocumentType-specific basis. So for example, if I have a Homepage doc type, I could create:
public HomepageController : RenderMvcController
{
public override ActionResult Index(RenderModel model)
{
// Create your new renderModel here, inheriting
// from RenderModel
return CurrentTemplate(renderModel);
}
}
This would route all calls to the homepage through this one action. For this, you don't need to define any new routes in the route table. And you should override the render model in the action not in the constructor.
Your question is slightly confusing and it's not entirely clear what you are trying to achieve because:
You have defined a route, and
In your constructor you are calling Umbraco.TypedContent(1225) to retrieve a specific published node
So ... if the admin page you are trying to route has itself been published by Umbraco (and it doesn't sound like it has), the just create a new controller with the name of the page's document type and override the render model in the way described above.
However ... if your admin page hasn't been published by Umbraco and you just want the admin page to access node data, then you have a couple of options:
Create a surface controller, inheriting from SurfaceController. This will give you access to the Umbraco context et al; or
Create a standard controller (preferrably in an Area) and inject the ContentCache using something like Autofac
E.g.:
builder.RegisterControllers(typeof (AdminController).Assembly)
.WithParameter("contentCache", UmbracoContext.Current.ContentCache);
Create a standard controller (preferrably in an Area) and access the node using Umbraco's ContentService API, i.e. new Umbraco.Core.Services.ContentService().GetById(1225)
The difference between the last two approaches is that:
Injecting the ContentCache provides you readonly but very quick access to the published content.
Accessing the ContentService provides you read/write access to the nodes themselves but at the expense of speed as you are querying the database directly.
It depends on what your requirement is.
Either way, it is well worth taking time to read through the documentation for hijacking Umbraco routes, and at least trying to understand what is going on.

Well, I can tell you that your view isn't getting fed anything for the Razor markup because your Index method doesn't feed it anything. That's one problem. I can also tell you, that in your AddCouponCodesViewModel, you'll need an empty constructor, so that the razor syntax can just create an instance, and then populate it to match your submitted object to the view.
Modify your ViewController :
public ActionResult Index()
{
return View(viewModel);
}
Modify your AddCouponCodesViewModel to add an Empty constructor:
public AddCouponCodesViewModel()
{
}

Create a paramaterless constructor on your view model like this:
public AddCouponCodesViewModel():
this(new UmbracoHelper(UmbracoContext.Current).
TypedContent(UmbracoContext.Current.PageId))
{
}
This will get the contexts your other constructors are looking for.
After you've created a class with specific constructors, the compiler stops generating a parameterless one by default. Since you need a parameterless constructor, this is how to get one and still pass in the Umbraco contextual info your viewmodel needs

Related

ViewData in _LoginPartial

I have created ASP.NET Core application and now I'm trying to use ViewBag in _LoginPartial. I have created base controller:
public class BaseController : Controller
{
public ApplicationDbContext _db;
public BaseController(ApplicationDbContext db)
{
_db = db;
ViewData["MyKey"] = _db.MyTable.ToList();
}
}
All my controllers derive from this BaseController.
But when I see ViewData in _LoginPartial.cshtml, I can only see that it contains default Key=Title, Value=Home Page. What should I do to have MyKey available in _LoginPartial?
Thanks!
The problem is that you are trying to set ViewData content within the controller’s constructor.
The ViewData dictionary is not actually created by the controller. It is created at a very different point within the MVC pipeline and then gets injected into the controller. You can basically see this process as something like this:
// create controller
var controller = CreateController<MyController>();
// do stuff
// inject ViewData
controller.ViewData = GetViewDataDictionary();
// invoke controller action
var result = controller.SomeAction();
So the ViewData gets provided after the controller has been created; after its constructor ran. So any assignments to the view data within the constructor will not apply to the actual view data dictionary.
As such, you will need to set those values at a different time, you cannot use the constructor there. Using the ViewData in general is somewhat legacy construct that you should try to avoid if possible. Instead, you should work with strongly typed view model objects. Of course, this will require you to pass the data explicitly in each action, but that way you are also not introducing any implicit data flow.
An alternative, which is especially useful if what you are doing should actually always apply to the _LoginPartial, would be to use a view component. View components are reusable components that you can use inside of your views, which will behave similarly to a controller action. So you could just insert a view component into your partial, and have that run the logic (asynchronously even!) to provide the data from your database. And you wouldn’t need to mess with any of your controllers to make it work.
ViewData can be accessed after controller has been activated or by overrinding OnActionExecuting.
//
// Summary:
// Gets or sets Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary used by
// Microsoft.AspNetCore.Mvc.ViewResult and Microsoft.AspNetCore.Mvc.Controller.ViewBag.
//
// Remarks:
// By default, this property is intiailized when Microsoft.AspNetCore.Mvc.Controllers.IControllerActivator
// activates controllers.
// This property can be accessed after the controller has been activated, for example,
// in a controller action or by overriding Microsoft.AspNetCore.Mvc.Controller.OnActionExecuting(Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext).
// This property can be also accessed from within a unit test where it is initialized
// with Microsoft.AspNetCore.Mvc.ModelBinding.EmptyModelMetadataProvider.
[ViewDataDictionary]
public ViewDataDictionary ViewData { get; set; }
For a solution, you could try overring OnActionExecuting like below:
public class BaseController : Controller
{
public ApplicationDbContext _db;
public BaseController(ApplicationDbContext db)
{
_db = db;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
ViewData["MyKey"] = _db.Product.ToList();
base.OnActionExecuting(context);
}
}
_LoginPartial.cshtml
#foreach (Product item in #ViewData["MyKey"] as IList<Product>)
{
<li><a>#item.Name</a></li>
}

Asp. Net MVC private member value lost

I am me to asp. Ne MVC I am using N_layrs architecture ( usiness, service, validation, data and presintation layers). I created a controller to view some data. I implemented the presentation interface to the controller abd I alled the display method (this method wil retrieve the data from the system layers and call the presentation interface method to gi e me the value) in the implemented interface method I assine the back items variable to local private variable but when I assign this list to View method the value be null.
public class AgreementController : Controller, IListView<IList<AgreementModel>>
{
private static ListPresenter<AgreementModel> _agreementListPresenter;
//This is the private member
private IList<AgreementModel> _items;
public RequestType RequestType
{
get
{
return RequestType.FindAll;
}
}
public string ListErrorMessage
{
set
{
}
}
// GET: Agreement
public ActionResult Index()
{
//Some unity code for DI
IUnityContainer unityContainer = new UnityContainer();
unityContainer.RegisterType<IListView<IList<AgreementModel>>, AgreementController>();
unityContainer.RegisterType<Service<IBusinessService<AgreementModel>, AgreementModel>>();
unityContainer.RegisterType<IBusinessService<AgreementModel>, BusinessService<AgreementModel>>();
unityContainer.RegisterType<IRepository<AgreementModel>, AgreementRepository>();
unityContainer.RegisterType<Service<IBusinessService<PlanModel>, PlanModel>>();
unityContainer.RegisterType<IBusinessService<PlanModel>, BusinessService<PlanModel>>();
unityContainer.RegisterType<IRepository<PlanModel>, PlanRepository>();
unityContainer.RegisterType<IValidator<AgreementModel>, AgreementValidator<AgreementModel>>();
unityContainer.RegisterType<IValidator<PlanModel>, PlanValidator<PlanModel>>();
//Create the Presenter this will fire DisplayList
_agreementListPresenter = unityContainer.Resolve<ListPresenter<AgreementModel>>();
_agreementListPresenter.Display();
//The value here is NULL
return View(_items);
}
public void DisplayList(IList<AgreementModel> items)
{
//I recieved vale here and it is OK
_items = items;
}
}
}
This solved my issue
unityContainer.RegisterInstance<IListView<IList<AgreementModel>>>(this);
By this I regester the current controller object insted of createing new one
Quick answer.
It looks like you instantiate a new AgreementController via Unity, which presumably sets its own item list somewhere in your million classes.
But the View is sent the list of items from the Controller which you have called. Not the one you instanciated.
Long answer.
You should move all you dependency injection code to your App Startup or Global.ascx point. Its called when the app starts up, not when a page is requested.
Your AgreementController should have a constructor with parameters for the services it needs. Unity will create the controller and inject the services that you have registered in global.ascx

ASP.NET Access controller properties from AuthorizeAttribute method

I have created a base controller for an API using an MVC 4 project. Everything works as I want, but in the interests of efficiency, I want to be able to access some of the custom properties from my base controller from the OnAuthorization method. I need to perform some SQL queries to make sure the access token is valid and so on. Id rather make this query once and store the object as a property on the controller so that i can access it later without needing to do a query again.
In short, this is what i want to do.
[APIActionFilter]
public class APIBaseController : ApiController
{
public APIClient client;
public class APIActionFilter : System.Web.Http.AuthorizeAttribute
{
public override void OnAuthorization(HttpActionContext filterContext)
{
//get data from the database.
Controller.client = objectmadefromdb;
}
}
}
There must be a reference to this object passed somewhere?
The first comment was along the lines, but was not correct. I was able to get this working using the following
public override void OnAuthorization(HttpActionContext filterContext)
{
var controllerRef = filterContext.ControllerContext.Controller as APIBaseController;
controllerRef.userdata = new user("123");
}
I was now able to access the properties from the main controller. I was able to set some public properties on the APIBaseController object and directly assign values to them. Could of course use getters and setters or whatever.
To confirm it works i was able to create a new controller that inherited the base controller. From any action within that controller I was able to access the properties of the APIBaseController object and they were populated with the data i set in the OnAuthorization method.

MVC routing priority for shared views

My solution has shared views with similar names in different folder locations
~\Views\Shared\Discount.ascx
~\Views\Dashboard\Shared\Discount.ascx
I'm extending WebFormViewEngine to define a view engine for routing
public class AreaViewEngine : WebFormViewEngine
{
public AreaViewEngine() : base()
{
ViewLocationFormats = new[] {
"~/Views/Shared/{0}.ascx",
"~/Views/Dashboard/Shared/{0}.ascx"
};
MasterLocationFormats = new[] {
"~/Shared/{0}.master"
};
PartialViewLocationFormats = ViewLocationFormats;
}
}
This is causing issues for views with similar names. I want to set higher priority to ~/Views/Dashboard/Shared/{0}.ascx if the URL contains /Dashboard/
Anyone knows how to do it? or is aware of a better way to handle this situation?
You could override the FindView method in your custom ViewEngine class, and look for a match containing the URL fragment before falling back to the default behavior.
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
var fragment = ... // access URL via controllerContext
var preferred = ViewLocationFormats.Where(x => x.Contains(fragment)).ToList();
// return retult from preferred or fall back to base.FindView
}
I'm not a big fan of this approach, though. If you need a view to be "overridable", you could simply make its output conditional or dependent upon model values, such as by adding a DiscountView property to your view model and then using the value of this property to decide what your view should emit.
If all you desire is "clever naming clash handling" then I'd recommend using T4MVC instead. It allows you to reference your views specifically and without magic strings, resolving any ambiguities that multiple views of the same name might otherwise cause.

Creating an "Ambient Context" (UserContext) for an ASP.NET application using a static factory Func<T>

I have found out that I need the current logged in user data in nearly every class (controllers, view, HTML helpers, services and so on). So I thought about to create an "Ambient Context" instead of injecting an IUserService or the User directly.
My approach looks something like that.
public class Bootstrapper
{
public void Boot()
{
var container = new Container();
// the call to IUserService.GetUser is cached per Http request
// by using a dynamic proxy caching mechanism, that also handles cases where we want to
// invalidate a cache within an Http request
UserContext.ConfigureUser = container.GetInstance<IUserService>().GetUser;
}
}
public interface IUserService
{
User GetUser();
}
public class User
{
string Name { get; set; }
}
public class UserContext : AbstractFactoryBase<User>
{
public static Func<User> ConfigureUser = NotConfigured;
public static User ActiveUser { get { return ConfigureUser(); } }
}
public class AbstractFactoryBase<T>
{
protected static T NotConfigured()
{
throw new Exception(String.Format("{0} is not configured", typeof(T).Name));
}
}
Example usage:
public class Controller
{
public ActionResult Index()
{
var activeUser = UserContext.ActiveUser;
return View();
}
}
Is my approach correct or do I missing something? Do you have better solutions in mind?
UPDATE:
More Detail of the User class:
public class User
{
string Name { get; set; }
bool IsSuperUser { get; set;}
IEnumerable<AzManOperation> Operations { get; set}
}
In Controllers we need to check if an User is a SuperUser to only provide the SuperUser some extra functionality.
public class BaseController : Controller
{
private readonly IUserService _userService;
BaseControler(IUserService userService)
{
_userService = userService
}
public User ActiveUser
{
get { return _userService.GetUser(); }
}
}
In Views we check Operations to only show an edit or delete button if the user has the right to do so. A view never uses the DependencyResolver, but ViewBag or ViewModel. My idea here is to implementing a custom ViewBasePage and providing an ActiveUser property, so that Views have an easy accesss.
In HtmlHelpers we render controls depending on IsSuperUser and Operations (passing in the User object or using DependencyResolver).
In Service Classes we need those properties too. For instance to decide if a basket is valid or not (check if the User is allowed to buy articles that are not in a standard list). So the Service class depends on IUserService and calling GetUser().
In Action Filters to force the user to change his password (only if it is not a SuperUser and User.ForcePasswordChange is true). Here we use the DependencyResolver.
My wish is to have a more easily way to get the User object, instead of using DependencyResolver.Current.GetService().GetUser() or using things like ViewBag.ActiveUser = User.
The User object is an object that is almost everywhere needed to check permissions or the like.
In Views we check Operations to only show an edit or delete button if the user has the right to do so.
The view should not do this check. The Controller should return a view model to the view that contains boolean properties that state whether those buttons should be visible. Returning a bool with IsSuperUser already moves to much knownledge into the view. The view shouldn't know that it should show a certain button for a super user: that's up to the controller. The view should only be told what to display.
If almost all views have this code, there are ways to extract repetitive parts out of your views, for instance with partial views. If you're finding yourself repeating those properties over many view models, perhaps you should define an envelope view model (a generic view model that wraps the specific model as T). A controller can create its view model, while you create a service or cross-cutting concern that wraps it in your envelope.
In Service Classes we need those properties too. For instance to decide if a basket is valid or not
In this case you are talking about validation, which is a cross-cutting concern. You should use decorators to add this behavior instead.
This is MVC, right?
You're reinventing the wheel.
Add this method to your Global.asax.cs:
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
var authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
var ticket = FormsAuthentication.Decrypt(authCookie.Value);
var user = ticket.Name;
var identity = new GenericIdentity(user, "Forms");
var principal = new GenericPrincipal(identity, null);
Context.User = principal;
}
}
This example shows forms authentication which you can strip if you're using another mechanism. The key is these three lines:
var identity = new GenericIdentity(user, "Forms");
var principal = new GenericPrincipal(identity, null);
Context.User = principal;
GenericIdentity and GenericPrincipal can be replaced with anything you want as long as they implement the (trivial) IIdentity and IPrincipal interfaces. You can create your own implementations of these classes with whatever extra properties you need.
You can then access the authenticated user from all the things you listed - controllers, views, etc. - via HttpContext.Current.User (which is static).
If you created your own implementation of IPrincipal you can just cast that reference to your custom type.
You'll note that IPrincipal has a method called IsInRole, so you'd say:
if (HttpContext.Current.User.IsInRole("SuperUser"))
TL;DR - you are overengineering something ASP.NET has already solved, and I'd have an aneurysm if I saw the types you're proposing in a production application.
I think the easiest and maintainable solution is to create a static class CurrentUserProvider which has only one method Get(HttpContextBase) that returns the current user, behind the scene you can use the DependencyResolver to get the service that actually returns the user. Then where you need the CurrentUser you can call CurrentUserProvider.Get(context) and do whatever custom logic you need to perform.
The other solution that you are trying to do is injecting the service in the base controller constructor which is okay if you have handful of controllers, it would become an issue if you have quite a number of controllers and not all of the controllers requires that service. Writing tests for those controller would be such pain in the neck, because you have to create stubs/mocks for that service for all your controller tests. Maybe you can use property injection instead of constructor to address it.
You can use the same property injection for Filters too.
Now, the remaining two are the view and the helper. For View you can create special base class that inherits from WebViewPage/ViewPage and use the IViewActivator to inject the service and the same applies for the helpers, create helpers that inherits from system helpers and use those in your base controllers and views.
I think the second approach is bit cumbersome and it does not add that much value to do all those custom things.
So my suggestion is to go with the first.

Categories

Resources