Asp. Net MVC private member value lost - c#

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

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>
}

How to Gather incoming objects in a List Globally per Thread

I am sending an object of data to a controller that has to store each object in a list of objects each time the controller gets called until all objects are gathered and ready for processing.
I am not very good with terminology but i think this is called buffering.
I have this:
public class MyController : Controller
{
private readonly List<int> _objectList;
public MyController()
{
if (_objectList == null)
{
_objectList = new List<int>();
}
}
[HttpPost]
public void Index(int newObject)
{
_objectList.Add(newObject);
Console.Write($"object {newObject} added");
}
}
So every time post data to the controller _objectList always starts null. How can i keep saving all data that goes into Index as long as the application is running.
Controllers are created and recycled for each request so that's why _objectList always starts null.
If you want to save all data that goes into Index as long as the application is running you could use ASP.NET Application State which is accessed through the Application property on the HttpContext object. This object will be available across all sessions. If you want a separate _objectList per session you would use Session State which is accessed through the Session property on the HttpContext object.
I would initialize MyController with dependency injection. This keeps the controller responsibility to handling the incoming data but not worry about how the data is stored. Something along the lines of this..
public class MyController
{
private IStoreInts storage;
public MyController(IStoreInts storage){this.storage = storage;}
[HttpPost]
public void Index(int value){
storage.Add(value);
}
}

How IAclModule works

I want to know how exactly this class IAclModule works, I mean the process it pass, it's instanceaded one each loop to verify the access for the user, or it's used the same instance each time?
I ask that because I need to implement my own logic since the default AuthorizeAttributeAclModule and XmlRolesAclModule have been wayyy to slow.
Thx.
The AclModule is instantiated by the DI (dependency injection) container. When using the internal DI container, it is instantiated by the SiteMapFactoryContainer.ResolveAclModule method. However, whether using internal or external DI, the instance is kept alive for the lifetime of the SiteMap object (and therefore by the cache) by being stored in a private field of the SiteMapPluginProvider class, which in turn is in a private field of the SiteMap class. Therefore, there is 1 instance per SiteMap which by default only gets created each time the cache expires (1 time every 5 minutes by default).
The AuthorizeAttributeAclModule has been tested pretty thoroughly and has been optimized pretty well. However, it creates an instance of the related controller class and the AuthorizeAttribute class for each node, so if your Controllers or any custom implementations of AuthorizeAttribute are doing too much work in the constructor, you could run into performance issues.
To fix this, you should aim to inject dependencies into each controller through the Controller constructor instead of doing heavy lifting inside of the constructor. If you use a dependency injection container in conjunction with a custom IControllerFactory, you can control the lifetime of the dependencies externally from the controller. As shown in the example below, when using this approach you could use the same instance of IMyRepository for every instance of MyController.
public class MyController : Controller
{
public MyController(IMyRepository repository)
{
if (repository == null)
throw new ArgumentNullException("repository");
this.repository = repository;
}
private readonly IMyRepository repository;
public ActionResult Index()
{
var items = this.repository.GetList()
return View(items);
}
}
That is a recommended best practice for the design of your controllers, but in a pinch, you could also request-cache your dependencies if they are expensive to create so each controller instance won't be so time-consuming to create if it is instatiated more than one time per request.
public class MyController : Controller
{
public MyController()
{
this.repository = this.GetOrCreateRepository();
}
private readonly IMyRepository repository;
private IMyRepository GetOrCreateRepository()
{
var key = "MyControllerRepository";
var result = HttpContext.Items[key];
if (result == null)
{
// If the expensive dependency wasn't already created for this request, do it now
result = new MyRepository();
// Save the instance in the request, so the next time this controller is created,
// it doesn't have to instantiate it again.
HttpContext.Items[key] = result;
}
return result;
}
public ActionResult Index()
{
var items = this.repository.GetList()
return View(items);
}
}
Also, if you have custom AuthorizeAttribute(s) you should make sure they are not doing any work except checking whether the user is authorized, delegating the real work to a handler the same way that Microsoft does.

Resolving a class with a custom parameter in Simple Injector

I'm creating a WPF MVVM application using Simple Injector as DI container. Now I'm having some issues when I'm trying to resolve a view from Simple Injector, because I'm in need of passing a parameter into my constructor at construction time (not when registering the view to the container, thus this is not applicable: Simple Injector pass values into constructor).
What I'm after is something like this:
var item = container.GetInstance<MyType>(myParameter);
I've read several places that this is not possible in Simple Injector because it should not be done (including here: https://simpleinjector.codeplex.com/discussions/397080).
Is this true, and if so, how could I do this instead?
Background information
I have a collection of multiple view models and models which are looked up by a specific key, and the parameter I want to pass into the view is the key for the view model to use. I've found this necessary because the view models and the models are used in multiple locations of the application, and need to stay in sync / be the same instances if they have the same key. I don't think I'm able to use lifetime scope to solve this, and there is no way I know the keys when I'm registering to the container. I've also understood that the ViewModelLocator approach might be the ServiceLocator (anti-?)pattern, however, currently it is the best I've got.
My constructor currently looks like this, and I want the IViewModelLocator to be resolved, while I pass in the key:
public FillPropertiesView(IViewModelLocator vml, object key)
{
// .. Removed code
// Assign the view model
var viewModel = vml.GetViewModel<FillPropertiesViewModel>(key);
DataContext = viewModel;
}
The IViewModelLocator looks like the following (and a similar interface exists for models).
public interface IViewModelLocator
{
// Gets the view model associated with a key, or a default
// view model if no key is supplied
T GetViewModel<T>(object key = null) where T : class, IViewModel;
}
Now I have the following questions:
What is the best way to be able to resolve the view with the view model key?
Do I have to do some refactoring to enable this?
Am I missing out on some of the power of the DI container since I have created my own dictionary based ViewModelLocator?
Extended information
I have shown the ViewModelLocator above, and the reason I'm using this is to keep blendability (basically it just provides me with design time data when opened in Blend). The runtime view model could have been the same for every instance of the view (not dependent on the key), if I did not have to have different windows opened at the same time (see next paragraph). The issue described above, however, is the same when the ViewModel is fetching a model, and the ModelLocator needs a key to fetch an existing model if it exists.
This is part of a VSTO application targeting PowerPoint (which affects some parts of the design). When an object on screen is selected, a task panel is opened (this is basically the FillPropertiesView explained above). The object that is selected has a key that is supplied to the view, in order for the view to extract the correct view model from the ViewModelLocator. The view model will then get a reference to a model by using a IModelLocator (similar to the IViewModelLocator) and the same key. At the same time, a controller will fetch the model from the ModelLocator by using the same key. The controller listens to change events from the model and updates the objects on screen. The same process is replicated whenever a new object is selected, and at the same time multiple windows could be open that interacts with the same or different objects simultaneously (that is, with multiple task panes all with unique view models).
Up until now I have resolved the view with a default, parameterless constructor, and then injected the view model with a method call afterwards:
// Sets the view model on a view
public void SetViewModel(IViewModelLocator vml, object key)
{
// Assign the view model
_viewModel = vml.GetViewModel<FillPropertiesViewModel>(key);
DataContext = _viewModel;
}
I never had to register the view with the container, but resolved the concrete type like this:
string key = "testkey" // read from the selected object
var view = container.GetInstance<FillPropertiesView>();
var vml = container.GetInstance<IViewModelLocator>();
view.SetViewModel(vml, key);
This issue surfaced when I tried to refactor this so that I did not have to call the SetViewModel() method every and manually resolve the view models etc. It got very messy when I also had to do this manual initiation within the view model to initiate the model in the same way.
ViewModelLocator
The ViewModelLocator is currently working as a wrapper around the DI container, i.e. the view models are registered in Simple Injector.
The registrations are like the following (in a class called CompositionHost):
container.RegisterSingle<IViewModelLocator, ViewModelLocator>();
container.RegisterSingle<IModelLocator, ModelLocator>();
The implementation looks like this:
// Base implementation used by ViewModelLocator and ModelLocator
public class ServiceLocator<TService> where TService : class
{
private readonly Dictionary<CombinedTypeKey, TService> _instances =
new Dictionary<CombinedTypeKey, TService>();
// Gets a service instance based on the type and a key.
// The key makes it possible to have multiple versions of the same service.
public T GetInstance<T>(object key = null) where T : class, TService
{
var combinedKey = new CombinedTypeKey(typeof(T), key);
// Check if we already have an instance
if (_instances.ContainsKey(combinedKey))
{
return _instances[combinedKey] as T;
}
// Create a new instance
// CompositionHost is a static reference to the DI container (and
// should perhaps be injected, however, that is not the main issue here)
var instance = CompositionHost.GetInstance<T>();
_instances.Add(combinedKey, instance);
return instance;
}
// A combined key to ease dictionary operations
private struct CombinedTypeKey
{
private readonly object _key;
private readonly Type _type;
public CombinedTypeKey(Type type, object key)
{
_type = type;
_key = key;
}
// Equals and GetHashCode() are overridden
}
}
public class ViewModelLocator : IViewModelLocator
{
private readonly ServiceLocator<IViewModel> _viewModelLocator;
public ViewModelLocator(ServiceLocator<IViewModel> locator)
{
_viewModelLocator = locator;
// Dummy code that registers design time data is removed
}
// IViewModel is just an empty interface implemented by the view models
public T GetViewModel<T>(object key = null) where T : class, IViewModel
{
return _viewModelLocator.GetInstance<T>(key);
}
}
Injecting a service locator into your classes is (almost) never the way to go, because this disallows compile time checking of dependencies and runtime dependency analysis. For that reason I can also advice to register ALL your root types (such as your views) since otherwise Simple Injector is left in the dark and is not able to advice you about any possible misconfigurations that you might have.
Since you have View + ViewModel pairs that are always cached together, but might depend on Model instance that are reused by multiple View + ViewModel pairs, I suggest the following design.
Define an abstraction for views and view models:
public interface IView<TModel>
{
IViewModel<TModel> ViewModel { get; }
}
public interface IViewModel<TModel>
{
TModel Model { get; set; }
}
Define an abstraction for retrieving/caching view by key.
public interface IViewProvider<TView, TModel> where TView : IView<TModel>
{
TView GetViewByKey(object key);
}
With these abstractions your view can look as follows:
public class FillPropertiesView : IView<FillPropertiesModel>
{
public FillPropertiesView(FillPropertiesViewModel viewModel)
{
this.ViewModel = viewModel;
}
public IViewModel<FillPropertiesModel> ViewModel { get; private set; }
}
And your controllers can depend upon the IViewProvider<TView, TModel> abstraction so they can reload the view when a new key is coming in:
public class FillPropertiesController : Controller
{
IViewProvider<FillPropertiesView, FillPropertiesModel> viewProvider;
FillPropertiesView view;
public FillPropertiesController(
IViewProvider<FillPropertiesView, FillPropertiesModel> provider) {
this.viewProvider = provider;
}
public void Reinitialize(object key) {
this.view = this.viewProvider.GetViewByKey(key);
}
}
The implementation for IViewProvider<TView, TModel> could look like this:
public class ViewProvider<TView, TModel> : IViewProvider<TView, TModel>
where TView : class, IView<TModel> {
Dictionary<object, TView> views = new Dictionary<object, TView>();
Container container;
IModelProvider<TModel> modelProvider;
public ViewProvider(Container container,
IModelProvider<TModel> modelProvider) {
this.container = container;
this.modelProvider = modelProvider;
}
public TView GetViewByKey(object key) {
TView view;
if (!this.views.TryGetValue(key, out view)) {
this.views[key] = view = this.CreateView(key);
}
return view;
}
private TView CreateView(object key) {
TView view = this.container.GetInstance<TView>();
view.ViewModel.Model = this.modelProvider.GetModelByKey(key);
return view;
}
}
This implementation depends on a (previously undefined) IModelProvider<TModel> abstraction. This is basically your old ModelLocator, but by using a generic type you can make the implementation much easier, because we can have one instance of this type per TModel (the same holds for ViewProvider), which saves you from having to do things with storing elements with the { Type + key } combination.
You can register this all as follows:
Assembly asm = Assembly.GetExecutingAssembly();
container.RegisterManyForOpenGeneric(typeof(IView<>), asm);
container.RegisterManyForOpenGeneric(typeof(IViewModel<>), asm);
container.RegisterOpenGeneric(typeof(IViewProvider<,>),
typeof(ViewProvider<,>), Lifestyle.Singleton);
container.RegisterOpenGeneric(typeof(IModelProvider<>),
typeof(ModelProvider<>), Lifestyle.Singleton);
var controllers =
from type in asm.GetTypes()
where type.IsSubClassOf(typeof(Controller))
where !type.IsAbstract
select type;
controllers.ToList().ForEach(t => container.Register(t));
container.Verify();
With RegisterManyForOpenGeneric you let Simple Injector search the supplied assemblies to look for implementations of the given open generic abstraction and Simple Injector will batch-register them for you. With RegisterOpenGeneric you specify an open-generic abstraction and tell Simple Injector which implementation to use when a close-generic version of that abstraction is requested. The last line searches through your application to look for all controller types and registers them in the system.

System.NullReferenceException creating viewModel

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

Categories

Resources