How to initialise BaseModel in BaseController - c#

I have a class of properties which are set from a service which I need available on every view of my MVC application.
Therefore I've created a "Base View Model" which my view models will inherit from.
public class BaseModel
{
public BaseModel()
{
foo = "foo value";
bar = "bar value";
}
public string foo { get; set; }
public string bar { get; set; }
}
public class HomeIndexViewModel : BaseModel
{
}
I have then created a "Base Controller" which all my controllers will inherit from:
public class BaseController : Controller
{
public BaseController()
{
}
}
public class HomeController : BaseController
{
public ActionResult Index()
{
HomeIndexViewModel model = new HomeIndexViewModel();
return View(model);
}
}
This is working as expected and I can call #Model.foo in my view and get foo value.
However I don't believe I should be initialising the values of BaseModel in it's constructor as this isn't using Dependency Injection and will become difficult to unit test.
How can I move the setting of the values foo and bar into the BaseController?
Of course I could set the values in the HomeController, but I would rather abstract this from the controller as the logic will always be the same and would bloat all my controllers.

I think the problem is that you are creating the instance of your models inside of the action, so the base controller has no reference to the object to set the properties.
Personally I would probably opt for some 'factory-type' function in the base controller that is responsible for creating the models as you need them.
Something like this for example:
public class BaseController : Controller
{
public T CreateBaseModel<T>() where T : BaseModel, new()
{
return new T
{
foo = "foo value",
bar = "bar value"
};
}
}
Then when you create your models in the actions you can do them like this:
HomeIndexViewModel model = CreateBaseModel<HomeIndexViewModel>();
If for some reason you need to pass parameters to your model constructor then you can have an overload like this:
public T CreateBaseModel<T>(params object[] args) where T : BaseModel
{
T model = (T)Activator.CreateInstance(typeof(T), args);
model.foo = "foo";
return model;
}
HomeIndexViewModel model = CreateBaseModel<HomeIndexViewModel>(param1, param2, etc);
Alternative
The main benefit of the above method is that you can access the foo and bar properties within the action code. However, if you don't care about this and only need the values to be accessible from within the View page, then you can override the OnActionExecuted method and apply the values in there. The benefit of this approach is that you don't need to change the way your models are created in the actions...
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
BaseModel model = filterContext.Controller.ViewData.Model as BaseModel;
if (model != null)
{
model.foo = "foo value";
model.bar = "bar value";
}
base.OnActionExecuted(filterContext);
}
Having the null check in there means it will only try to apply the values for models that inherit from BaseModel, which means you can still use other models without worry.
With this approach, your action code goes back to how it was originally:
HomeIndexViewModel model = new HomeIndexViewModel();
return View(model);

Related

Set/Change Display(Name="") Attribute of a Property in Controller/ViewComponent

According to this answer from 2010, regarding mvc-2, it wasn't possible. What about now, in asp.net-core 2.2?
My usecase:
I have a BaseViewModel that is being used by 2 views: TableView (for users) and TableManagentView (for admins). The BaseViewModel is invoked by a ViewComponent. Here are some samples:
BaseViewModel:
public class BaseViewModel {
[Display(Name = "Comment")
public string UserComment { get; set; }
}
TableView:
#await Component.InvokeAsync(nameof(Base), new { myObject = myObject, stringName = "User"})
TableManagementView:
#await Component.InvokeAsync(nameof(Base), new { myObject = myObject, stringName = "Admin"})
Base:
public class Base : ViewComponent
{
public IViewComponentResult Invoke(BaseViewModel myObjet, string invokingView)
{
// here i want to do something like that
if (invokingView == "User") {
myObject.UserComment.SetDisplayName("My Comment");
}
if (invokingView == "Admin") {
myObject.UserComment.SetDisplayName("User's Comment");
}
return View("BaseViewComponent", myObject);
}
}
BaseViewComponent:
#Html.DisplayNameFor(model => model.UserComment)
The BaseViewModel is simplified, but there are a lot more attributes. The reason I want to do this is to avoid code duplication in both tables. The only thing that should change are the label names.
I've tried reflection, but without success:
public IViewComponentResult Invoke(BaseViewModel myObject, string invokingView)
{
MemberInfo property = typeof(BaseViewModel).GetProperty("UserComment");
property.GetCustomAttributes(typeof(DisplayAttribute)).Cast<DisplayAttribute>().Single().Name = "test";
return View("BaseViewComponent", myObject);
}
The Name doesn't change and remains "Comment" from the initial setting.
If it's not possible to set the attribute name programmatically, what other solutions do I have? I'm thinking about ViewBag/ViewData or TempData, but this solution doesn't appeal to me. What would be the pro's and con's of that?
Extending on the comment I left, one way you could solve this is by having your BaseViewModel being an abstract class and have concrete classes deriving from it. So UserViewModel and AdminViewModel. These two concrete classes would then be the models for both TableView and TableManagentView and would be responsible for telling the "outside world" how to label fields.
The base class has two main aspects (apart from your normal fields): An abstract Dictionary<string, string> which will contain the labels and a method to get the label from the list: string GetLabel(string propName). So something like this:
public abstract class BaseViewModel
{
protected abstract Dictionary<string, string> Labels { get; }
public string UserComment { get; set; }
public string GetLabel(string propName)
{
if (!Labels.TryGetValue(propName, out var label))
throw new KeyNotFoundException($"Label not found for property name: {propName}");
return label;
}
}
Then you create the two deriving classes User and Admin:
public sealed class UserViewModel : BaseViewModel
{
protected override Dictionary<string, string> Labels => new Dictionary<string, string>
{
{ nameof(UserComment), "User label" }
};
}
public sealed class AdminViewModel : BaseViewModel
{
protected override Dictionary<string, string> Labels => new Dictionary<string, string>
{
{ nameof(UserComment), "Admin label" }
};
}
They only implement the Dictionary<string, string> and set the appropriate text for each field on the base class.
Next, changing your BaseViewComponent to this:
View:
#model DisplayNameTest.Models.BaseViewModel
<h3>Hello from my View Component</h3>
<!-- Gets the label via the method on the base class -->
<p>#Model.GetLabel(nameof(BaseViewModel.UserComment))</p>
<p>#Model.UserComment)</p>
ComponentView class (simpler now)
public IViewComponentResult Invoke(BaseViewModel viewModel)
{
return View(viewModel);
}
Finally, changing your views TableView and TableManagentView to this:
#model WebApp.Models.AdminViewModel
#{
Layout = null;
}
<h1>Admin View</h1>
<div>
#await Component.InvokeAsync("Base", Model)
</div>
and the Controller to:
public IActionResult Index()
{
var adminViewModel = new AdminViewModel { UserComment = "some comment from admin" };
return View(adminViewModel);
}
Now when you navigate to TableView, you'll pass a UserViewModel to the BaseViewComponent and it will figure it out the correct label. Introducing new fields will just now require you to change your viewmodels, adding a new entry to the Dictionary.
It's not perfect, but I think it's an okay way to solve it. I'm by far not an MVC expert so maybe others can come up with a more natural way to do it as well. I also prepared a working sample app and pushed to GitHub. You can check it out here: aspnet-view-component-demo. Hope it helps somehow.

ASP.NET MVC, Entity Framework: Generic model-independent controller class implemented with actions

I'm trying to implement this kind of mechanizm:
Model has bit field: is_active, so I'm using it to toggle visibility on the main form. The action in controler is like:
public ActionResult Toggle(int id)
{
Country country = _db.Countries.Find(id);
country.is_active = country.is_active == null ? true : !country.is_active;
_db.SaveChanges();
return RedirectToAction("Index");
}
I want my admin lookup controllers to derive from that generic class and use the implementation from it for common actions like toggle.
I've already created ITogglable interface that has ActionResult Toggle(int id) so I'm implementing it inside particular controler (CountryController : Controller, ITogglable), but I have to implement it every time. Instead I would more like my CountryController, RegionController, etc derive from a generic class (i.e. Togglable) that has these kind of methods already implemented like:
public virtual ActionResult Toggle(int id)
{
CurrentModelTypeUsedInController lookup = _db.CurrentModelTypeUsedInController.Find(id);
lookup .is_active = lookup .is_active == null ? true : !lookup.is_active;
_db.SaveChanges();
return RedirectToAction("Index");
}
Is this even possible? I have no idea how to make it lookup-independant so I do not have to provide type of CurrentModelTypeUsedInController each time.
You need a couple of abstractions to achieve this.
Firstly you will need an abstraction that defines is_active, such as
public interface IActive
{
bool is_active { get; set; }
}
And all of the types such as Country and CurrentModelTypeUsedInController will need to implement this abstraction
public class Country : IActive
{
public bool is_active { get; set; }
}
And with this in place we define the generic controller. The key point here is the generic constraint (IActive) that is placed on generic type (TModelType). By defining that all types of TModelType must implement IActive the code knows that the type is guaranteed to expose a property named is_active
public class AbstractController<TModelType> : Controller
where TModelType : class, IActive
{
private readonly DbContext _db;
public AbstractController()
{
_db = new DbContext("connection");
}
public ActionResult Toggle(int id)
{
TModelType instance = _db.Set<TModelType>().Find(id);
instance.is_active = instance.is_active == null ? true : !instance.is_active;
_db.SaveChanges();
return RedirectToAction("Index");
}
}
And each controller can derive from AbstractController<>
public sealed class CountryController : AbstractController<Country>
{
}

Where should I place my domain model to view model mapping code?

Currently I use private static methods in my controller file to map domain model to view model and vice-versa. Like below:
public ActionResult Details(int personID)
{
Person personDM = service.Get(personID);
PersonViewModel personVM = MapDmToVm(personDM);
return View(personVM);
}
private static PersonViewModel MapDmToVm(Person dm)
{
PersonViewModel vm;
// Map to VM
return vm;
}
Is there any other standard way to do this?
I prefer to put the mapping logic inside the view model (dto) class, because we want to keep the domain model as clean as possible and also the domain model might change overtime.
public class Person
{
public string Name {get; set;}
}
public class PersonViewModel
{
public string Text {get; set;}
public static implicit operator PersonViewModel(Person dm)
{
var vm = new PersonViewModel { Text = dm.Name };
return vm;
}
public static implicit operator Person(PersonViewModel vm)
{
var dm = new Person { Name = vm.Text };
return dm;
}
}
and use it in the controller without explicit casting.
Person dm = service.Get(id);
PersonViewModel vm = dm;
Since the mapping is not always trivial, I think that it might be better to separate it into a different class other than the viewmodel.
That way each class has its own single responsibility. You might want to add an extension method to your domain model, something like:
public static MyViewModel ToViewModel(this MyDomainModel model)
{
// mapping code goes here
}
You also might consider using automapper and call its Map method from your controller.

How do I resolve type dependencies in regards of controller action?

Update
I've managed to create something that is satisfactory. You can see the relevant parts of the code here on PasteBin. If there is there something that I could improve please let me know. I've got a nagging feeling this isn't very efficient.
Clarification
While it certainly seems to work with static dependencies as suggested by d_z, I was hoping, to avoid instantiating objects not utlilized, that I could use something similar to this:
public class HomeController : Controller
{
[Dependency]
protected IBaseData ActionData { get; set; }
public ActionResult Index()
{
return View(ActionData);
}
public ActionResult About()
{
return View(ActionData);
}
}
The data in the IndexData and AboutData instances in reality aren't static. The instance properties are set with data from a database. The DbProvider is injected into these classes.
In the end what I want is to minimize the memory footprint and database accesses.
Original
Let's say we have the following basic controller (with corresponding views):
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
return View();
}
}
We also have two classes with the same interface:
public class IndexData : IBaseData
{
public string Name { get; set; }
public string Index { get; set; }
public IndexData()
{
Name = "Index";
Index = "This is an Index instance";
}
}
public class AboutData : IBaseData
{
public string Name { get; set; }
public string About { get; set; }
public AboutData()
{
Name = "About";
About = "This is an About instance";
}
}
What I'd like is for Unity to inject an instance of one of these classes, depending on which action is executed, into the corresponding view. I've tried to get my head around how to achieve this but I'm stumped. Is this even possible?
What you are requesting makes little sense. Dependency Injection is about injecting (design time) behavior (a.k.a. services). What you are trying to do however is to inject runtime data. So this is not a task that yout IoC container should handle.
Next the view should be completely ignorant of any dependency injection. The controller should return all data that the view needs from its action method. Make sure that your About and Index action methods return the proper instance.
To register several mappings for a type in Unity you have to create named registration like this:
myContainer.RegisterType<IBaseData, IndexData>("Index");
myContainer.RegisterType<IBaseData, AboutData>("About");
So after this in your actions you can resolve an instance accordingly:
Index:
IBaseData data = myContainer.Resolve<IBaseData>("Index");
About:
IBaseData data = myContainer.Resolve<IBaseData>("About");
Or for static dependencies it works like this:
[Dependency("Index")]
IBaseData data { get; set; }
Take a look here and here for details

Casting Object depending on its type

I made a validation method for business rules that are not verified from my rules in the model, but I'm having a problem to make it work. Since there are two possible scenarios(customer or seller registration), they need to be treated separately in their own views and models. The seller registration inherits from customer registration for the basic info, so all fields in customer are also in seller. But since I'm working with 2 different models, even though both have the same fields that I'm doing the validation upon, I needed to use Object to use the same validation method. But unfortunately I'm having trouble to do so.
[CustomHandleError]
private bool ValidateRegistrationForm (Object registerViewModelObject) {
if (registerViewModelObject is RegisterViewModel)
{
RegisterViewModel registerViewModel =
(RegisterViewModel)registerViewModelObject;
}
else
{
RegisterSellerViewModel registerViewModel =
(RegisterSellerViewModel)registerViewModelObject;
}
if (ModelState.IsValid)
{
string [] names = registerViewModel.Name.Split (
new string [] {" "}, StringSplitOptions.RemoveEmptyEntries);
if (names.Length == 1)
ModelState.AddModelError ("Name", "Fill your full name");
if (CustomerUtilities.IsCpf (registerViewModel.Identity) == false)
ModelState.AddModelError ("Identity", "Invalid CPF value");
if (this.AuthenticatorService.IsExistentUser (registerViewModel.Email))
ModelState.AddModelError ("Email", "Email already registered");
}
}
As you can see, after the if (ModelState.IsValid) the IntelliSense doesn't find registerViewModel in the current context. I wonder why this happens, since that variable is defined inside the if AND the else above, so there is no way to reach that code without it being defined.
Is there any workaround for this(other than creating a new method or passing 2 variables)?
Declare RegisterViewModel outside of the if block scope, and assign it within the if block.
RegisterViewModel registerViewModel;
if (registerViewModelObject is RegisterViewMOdel)
{
registerViewModel = // ...
}
else
{
registerViewModel = // ...
}
If you wish to have two separate variables then declare both outside of the if statement and test for null after.
RegisterViewModel registerViewModel;
RegisterSellerViewModel sellerModel;
if (registerViewModelObject is RegisterViewModel)
{
registerViewModel = (RegisterViewModel)registerViewModelObject;
}
else
{
sellerViewModel = (RegisterSellerViewModel)registerViewModelObject;
}
However, defining an interface to use instead of Object would be the better option.
public interface IRegisterViewModel
{
public string Name { get; set;}
public ... Identity {get; set;}
...
}
public class RegisterViewModel : IRegisterViewModel
{
...
}
public class RegisterSellerViewModel : IRegisterViewModel
{
...
}
Then use ValidateRegistrationForm(IRegisterViewModel registerViewModel) and you can get rid of the if statement entirely.
You should define RegisterViewModel outside from your if statemtent. And make assignment inside your if statement.
Like;
RegisterViewModel registerViewModel;
if(...)
{
//make your assigment here.
}
You probably need to extract methods that are common for RegisterViewModel and RegisterSellerViewModel into an interface and implement it in both classes. Then cast registerViewModelObject to this interface regardless of its actual type.
The problem occurs since you don't have a single variable defined in the main scope of the function. In the way that you have written your code, you define two variables that are inside different scopes.
How I would go about with the solution:
I would make a base class.
class RegisterModel
{
public string Name;
public IdentifyType Identify;
public string Email;
}
And then both your classes can inherit from the base class. Like this:
class RegisterViewModel
: RegisterModel
{...}
class RegisterSellerViewModel
: RegisterModel
{...}
Now you can actually covert the Object variable in your function a single time. Like this:
private bool Validate(Object viewModel)
{
var castViewModel = (RegisterModel)viewModel;
if(ModelState.IsValid)
{
...
}
}
Note that this will cause a run-time error if viewModel is not of type RegisterModel.

Categories

Resources