Pattern for passing common data to _layout.cshtml in MVC4.5 - asp.net-mvc-4

I am trying to come up with the best pattern for passing data to my _layout.cshtml page.
I am toying with creating a common base class from which all view specific models derive. This base class would be recognized by my _layout.cshtml and used to fill in details about the user and load proper images in the header, etc. For example, here is a snippet of it.
public abstract class ViewModelBase
{
public string Username { get; set; }
public string Version { get; set; }
}
At the top of my _layout.cshtml I have...
#model MyProject.Web.Controllers.ViewModelBase
I need a common area to hydrate the information required by the model, and am planning to use the following pattern...
Each action method creates and hydrates a model derived from
ViewModelBase.
The action completes.
I create a ActionFilterAttribute and override OnActionExecuted to cast the
current Result to ViewModelBase.
If the conversion is successful, then I populate the ViewModelBase details with the relevant data.
Here are my questions...
Is the use of a ActionFilterAttribute (OnActionExecuted) a good pattern for what I am trying to do?
I am not able to see how to get the Result created in the action from the HttpActionExecutedContext. How is this done?

I follow the same approach and use a base ViewModel class which all my other viewModels inherit from.
Then, I have a base controller that all controller inherit from. In there, I have one method that takes care of initializing the view model:
protected T CreateViewModel<T>() where T : ViewModel.BaseViewModel, new()
{
var viewModelT = new T {
HeaderTitle = "Welcome to my domain",
VisitorUsername = this.VisitorUsername,
IsCurrentVisitorAuthenticated = this.IsCurrentVisitorAuthenticated,
//...
};
return viewModelT;
}
Then on each controller, when I want to create the view model, I simply call the base controller's method:
var vm = base.CreateViewModel<MyPageCustomViewModel>();

Related

Instantiating ModelExpression directly

Let's say I have the following input tag which utilizes the built-in tag helper:
#model ProductViewModel
<label asp-for="Product.Id"></label>
In my case, this expands into the following:
<label for="Product_Id">Id</label>
I see that asp-for is expecting a ModelExpression:
In tag helper implementations, I often see a property like the following:
public ModelExpression For { get; set; }
It appears that this is automatically populated when the tag helper is used.
Is there a way to instantiate a ModelExpression directly in C#?
I.e. something like this:
var exp = new ModelExpression("Product.Id",...)
I'd like to be able to generate "Product_Id" and "Id" from Product.Id as the input tag helper did.
As far as I know, you can specify that your property is to be set to the name of some property on the View's Model object by declaring your property with the ModelExpression type. This will enable any developer using your property to get IntelliSense support for entering a property name from the Model object. More importantly, your code will be passed the value of that property through the ModelExpression's Model property.
Sample code as below:
[HtmlTargetElement("employee-details")]
public class EmployeeDetailTagHelper : TagHelper
{
[HtmlAttributeName("for-name")]
public ModelExpression EmployeeName { get; set; }
[HtmlAttributeName("for-designation")]
public ModelExpression Designation { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "EmployeeDetails";
output.TagMode = TagMode.StartTagAndEndTag;
var sb = new StringBuilder();
sb.AppendFormat("<span>Name: {0}</span> <br/>", this.EmployeeName.Model);
sb.AppendFormat("<span>Designation: {0}</span>", this.Designation.Model);
output.PreContent.SetHtmlContent(sb.ToString());
}
}
Code in the View page:
#model WebApplication7.Models.EmployeeViewModel
<div class="row">
<employee-details for-name="Name" for-designation="Designation"></employee-details>
</div>
Code in the Model
public class EmployeeViewModel
{
public string Name { get; set; }
public string Designation { get; set; }
}
From above code, you can see that we could custom the attribute name. More detail information about using the ModelExpression, check the following links:
Creating Custom Tag Helpers With ASP.NET Core MVC
Expression names
I'd like to be able to generate "Product_Id" and "Id" from Product.Id
as the input tag helper did.
Besides, do you mean you want to change the Product. Id to Product_Id, in my opinion, I'm not suggesting you change it, because generally we can use "_" as a separator in the property name. So, if we are using Product.Id, it means the Product's Id property, and the Product_Id means there have a Product_Id property.
To answer the question:
Is there a way to instantiate a ModelExpression directly in C#"
Yes you can, through IModelExpressionProvider and its CreateModelExpression method. You can get an instance of this interface through DI.
Now, if you're already in your view and working with tag helpers, Zhi Lv's answer is all you need, as the functionality is built-in and much easier to use. You only need IModelExpressionProvider for when you're in your Razor Page, Controller, or perhaps some custom middleware. Personally, I find this functionality useful for my Ajax handlers that need to return one of my ViewComponents that has a ModelExpression argument (so that I can easily call it from my Pages/Views too.)
To call CreateModelExpression, you'll need a strongly-typed instance of ViewData. In Razor Pages, this is as easy as casting the ViewData property to the strongly-typed instance of your PageModel's type (presuming you don't have a page model hierarchy):
var viewData = (ViewDataDictionary<IndexModel>)ViewData;
If you're using MVC and you're in the controller, that won't exist yet. Best you can do is make your own instance.
var viewData = new ViewDataDictionary<ErrorViewModel>(new EmptyModelMetadataProvider(),
new ModelStateDictionary());
Once you get your strongly-typed ViewData instance, you can obtain your desired ModelExpression like this, just like using a lambda expression in your views:
var myPropertyEx = _modelExpressionProvider.CreateModelExpression(viewData,
m => m.MyProperty);

Passing enum value as parameter .net core

I have a controller (ResponseResourceController) which Index Action is supposed to receive as parameter int and in the Index action - Parse enum value from Tempdata. Then this action redirects to another action with this two parameters.
public class ResponseResourceController : Controller
{
public async Task<IActionResult> Index(
int id)
{
var entityType = (EntityType)TempData["EntityType"];
var exists = await _responseResourceStringUiService.ResponseResourceStringExistsAsync(
id,
entityType);
return RedirectToRoute(!exists ?
RouteNames.ResponseResourceString_Home_Add :
RouteNames.ResponseResourceString_Home_Edit,
new {id, entityType});
}....
}
public class PublicationController : Controller
{
public async Task<IActionResult> Index()
{
var vm = new PublicationViewModel
{
...
};
TempData["EntityType"] = InboxEntityType.Publication;
........
}
My question is - is there more elegant way to achieve this without the enum value beeing in the url after the redirection.
I was thinking for example to create an abstract property of thids Enum Type in this ResponseResourceController
AND the other controller PublicationController(and othe that are going to do that) to inherit it and override the property with the right enum value and as the ResponseResourceController will have property I will not need the entity type as parameter in the Add and Edit Action.
Why don't just directly call the PublicationController and avoid creating an unnecessary redirect? You can distinguish directly in the frontend if it is a new or an existing record.
If you wanna use this method for creating new records then you should prefer to use a POST request instead of GET.
In short, multiple controllers does not work and url redirection is not necessary.
From the description I think what you really want is displaying the add/edit form base on an ID (with your temp entity type stored in somewhere). If this is what you want then single controller with multiple views should be able to handle that.
What will happen when you create multiple controllers?
The routing rules in the app has to be unique. If you have a ResponseResourceController and a PublicationController and each of them has the Index action, what will be the routes look like?
You probably need to define a route like /ResponseResource/Index for ResponseResourceController.Index() and a route like /Publication/Index for PublicationController.Index(). If there is a EntityType called Bob, you may need to define a route like /Bob/Index and create a BobController.
However, if you try to name a unique route for each of the EntityType, your route has to contain that piece of information (either in the route or in the query string). So, creating a dedicated controller for each EntityType cannot solve your problem.
Multiple views for multiple entity types
If you don't want to include the EntityType in the url, you may use ResponseResourceController.Index(int id) to get the entity ID and respond with different views for different EntityType:
[Route("/response-resource")]
public class ResponseResourceController : Controller
{
[HttpGet("")]
public async Task<IActionResult> Index(int id)
{
var entityType = (EntityType)TempData["EntityType"];
var exists = await _responseResourceStringUiService.ResponseResourceStringExistsAsync(id, entityType);
var viewName = exists ? "_AddEntity" : "_EditEntity";
var vm = new GenericViewModel
{
type = entityType,
/* other fields */
}
return View(viewName, vm); // so user may see a different view under different situation
}
}
And for the view model:
public class GenericViewModel
{
public EntityType type { get; set; }
}
You also need to define 2 views: _AddEntity and _EditEntity. In the above example, GenericViewModel contains the EntityType so you will know which entity you are handling.
Furthermore, you can even define a dedicated view for each EntityType and pass a dedicated view model to that view. e.g. PublicationViewModel will be the view model used in _AddPublication and _EditPublication views.
Expected behavior
When user requests for ResponseResource with and id, e.g. /response-resource?id=123, your application will render the view base on the id and the EntityType. No url redirection and url won't change.

Access to container of Simple Injector MVC views

In a Sitecore project I've integrated Simple Injector using this article
It uses sitecore pipelines and then uses a method in App_start
namespace BBC.App_Start
{
public class SimpleInjector : IPackage
{
public void RegisterServices(Container container)
{
GetContainer.RegisterServices(container);
container.Register(() => new SitecoreContext(), Lifestyle.Scoped);
container.Register(() => new Container(), Lifestyle.Singleton);
}
}
}
Simply I can inject container into controller constructor but can't have container in View files.
I tried to declare a static property in App-start and save container to it. but still I'm getting no registration type in Views
What is the best way to have container object in views?
As Stephen suggests in his comment, the literal answer to your question is "you shouldn't do that - because it's not really the way MVC and DI are supposed to work". The more detailed answer goes something like this:
The job of your view is to present data that it has been passed via the Model. Views should not really contain logic. Very simple stuff like "if flag is false, hide this block of mark-up" is ok, but the more complex code to work out what the value of the flag is shouldn't be in the view.
MVC tries to make our website code better by encouraging you to separate presentation (the View) from data (the Model) and logic (the Controller). This should make our code easier to work with - So if you have processing that needs doing, then it should really be happening when your controller method runs.
If your view requires some special data, best practice suggests it should work it out in the controller method and pass it to the view in the model. The code might look more like this:
public class MyModel
{
public string SpecialData { get; set; }
}
public class MyController : Controller
{
public ActionResult DoSomething()
{
// do whatever processing is needed
var somethingCalculate = resultFromYourOtherObject();
// do other stuff
var model = new MyModel() { SpecialData = somethingCalculated };
return View(model);
}
}
And then the View just needs to accept the MyModel class as its model, and render the SpecialData property - no logic required.
I think also it's considered a bad idea to have calls to fetch objects from your DI container spread about your codebase. For MVC apps, generally your DI container gets wired in to the process of creating a controller for a request when the app starts up. Rather than passing about a DI Container into your controllers, the DI framework extends the Controller-creation process, and the container isn't exposed outside of this. When the MVC runtime needs to create a controller, the controller-creation logic uses the DI framework to fetch objects for all the controller's dependencies.
Without more detail about what you actually want to achieve, it's difficult to say what the "right" approach to creating your object(s) here is, but the two most common patterns are probably:
1) Constructor injection: Your controller has a parameter which accepts the object required. The DI container creates this object for you at the point where it creates the controller, so your controller gets all its dependencies when it is created. Good for: scenarios where you know how to create the object at the beginning of the request.
public interface IMySpecialObject
{
string DoSomething();
}
public class MyController : Controller
{
private IMySpecialObject _specialObject;
public MyController(IMySpecialObject specialObject)
{
_specialObject = specialObject;
}
public ActionResult RenderAView()
{
// do some stuff
var data = _specialObject.DoSomething();
return View(data);
}
}
As long as IMySpecialObject and a concrete implementation for it are registered with your DI container when your app starts up, all is well.
2) Factory classes: Sometimes, however, the object in question might be optional, or it might require data that's not available at controller-creation time to create it. In that case, your DI framework could pass in a Factory object to your controller, and this is used to do the construction of the special object later.
public interface ISpecialFactory
{
ISpecialObject CreateSpecialObject(object data);
}
public class MyController : Controller
{
private IMySpecialFactory _specialFactory;
public MyController(IMySpecialFactory specialFactory)
{
_specialFactory = specialFactory;
}
public ActionResult RenderAView()
{
// do some stuff
if( requireSpecialObject )
{
var data = getSomeData();
var specialObject = _specialFactory.CreateSpecialObject(data);
var data = _specialObject.DoSomething();
return View(data);
}
return View("someOtherView");
}
}
But a good book on using DI may suggest other approaches that fit your specific problem better.

Getting form collection value in a common place

I have a service that I initialize on controller's contructor.
I want to pass UserName(this is my model's property) to this service while instantiating the service on contructor.
First question is, is it possible to get model on controller contructor? I tried but couldn't find any way to do this.
If not then the other way I thought of is to have a common function that will be called everytime any view action is executed, where I can access FormCollection and assign it to the service.
For this I tried overriding few controller's method like Initialize, OnExecuting etc. But I couldn't find form collection in them.
Is there any way to achieve this?
Edit
Some more description
private IService _service;
public HomeController()
{
_service = new ServiceImplementation(/*I want to pass UserName here*/);
}
public ActionResult Submit(MyModel model)
{
_service.UserName = model.UserName;
/* This UserName assignment part I want to centralize,
somewhere in the constructor or in any common event,
so that this will be initialized before any action method is called */
...
...
}
public ActionResult Delete(MyModel model)
{
_service.UserName = model.UserName;
...
...
}

Same view for both create and edit in MVC4

Can we have a single razor view for both Create and Edit operations?
If yes, how do we achieve this?
I don't recommend it.
This should be a rather long answer, because there's a lot of things involved in the process, request and workflow of a normal MVC GET/POST workflow. I will try to answer your question with the minimum information required and why I do not recommend the use of the same view.
First, why?
You don't have control over the views, which may have over-posting;
No flexibility;
Not reusable views or parts;
Hard to maintain the views (one change in the view must be tested on both actions).
My suggested approach would be to have different actions/views but share common code:
Create both views as normal.
You will have duplicated code, but not all code is the same, for example, you may not want to send an ID on the create action, this is not directly related to your question, but using the same view implies you are also sending the same data, and this is not recommended, especially for over-posting or mass assignment. More info about mass assignment here (an Architectural Approach is what I'm using here).
So let's start from what are you going to receive in your controllers.
In this case I used inheritance but it's not the only strategy.
Binding models
public class UpdateBindingModel : CreateBindingModel {
// since we are not using the same binding model,
// we can have a "real" validation rules on our update binding and view.
[Required]
public int? Id {get;set;}
}
public class CreateBindingModel {
// no id here prevent overposting.
[Required]
public string Name {get;set;}
[Required]
public int? CountryId {get;set;}
}
That will make sure the data you send to your Create and Edit is the minimum needed and nothing else.
Let's then see the View Models that will be sent to the View, for this example I will include a List that will be used to select some value but should not be posted (the list) to the controller, only the selected value.
View models
public class CreateViewModel : CreateBindingModel {
public IEnumerable<SelectListItem> CountryList {get;set;}
}
public class UpdateViewModel : UpdateBindingModel {
public IEnumerable<SelectListItem> CountryList {get;set;}
}
As you can see, this gives you lot of flexibility but still have some duplicated code (the extra information needed on view model for both views) which can be mitigated in several ways (depending the needs/context):
Have an action to retrieve the common data and using #Html.Action("GetCountryList");
Use the same View Model aka CreateUpdateViewModel and discarding extra UpdateBindingModel properties in the view but still posting the corresponding model on POST.
Having your binding models as properties and select one or the other in the specific view. (better use #Html.EditorFor instead of partials so Model Binder will work with no additional change on code)
The controller actions will look like:
Controller
[HttpGet]
public ActionResult Create(){
ViewData.Model = new CreateViewModel();
return View();
}
[HttpPost]
public RedirectToRouteResult Create(CreateBindingModel binding) {
// check valid model state and create data
return RedirectToAction("Index");
}
[HttpGet]
public ActionResult Update(int id) {
var objectToEdit = service.GetObjectToEdit(id);
ViewData.Model = new UpdateViewModel(objectToEdit);
return View();
}
[HttpPost]
public RedirectToRouteResult Update(UpdateBindingModel binding) {
// check valid model state and update data
return RedirectToAction("Index");
}
And your views:
Views
Update.cshtml
<form action="Update">
#Html.HiddenFor(Model.Id);
#Html.Partial("EditFieldsPartial")
<button>delete</button> // no delete button on create.
<button>create new</button> // you can have a create new instead of update.
</form>
Create.cshtml
<form action="Create">
#Html.Partial("EditFieldsPartial")
</form>
Note: code is incomplete and didn't use helpers in most cases for brevity and clarity. Do NOT copy paste :D
Sure you can.
On post, check in your controller whether the primary key has value 0 then Insert, otherwise Update.
View should be the same for Create and Edit.
Just remember to include:
#Html.HiddenFor(model=>model.ID)
In your view
For example:
Model:
public class DescriptionModel
{
[Key]
public int ID { get; set; }
public string Description { get; set; }
}
CreateEdit.cshtml:
#model DescriptionModel
#using (Html.BeginForm("CreateEdit"))
{
#Html.HiddenFor(model=> model.ID)
#Html.EditorFor(model=> model.Description)
<input type="submit" value='Submit' />
}
DescriptionModel controller:
public ActionResult Create()
{
return View("CreateEdit", new DescriptionModel());
}
public ActionResult Edit(int id)
{
return View("CreateEdit", db.DescriptionModels.Find(id));
}
// Submit and add or update database
[HttpPost]
public ActionResult CreateEdit(DescriptionModel model)
{
if (ModelState.IsValid)
{
// No id so we add it to database
if (model.ID <= 0)
{
db.DescriptionModels.Add(model);
}
// Has Id, therefore it's in database so we update
else
{
db.Entry(model).State = EntityState.Modified;
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
A View can definitely be shared for create and edit operations, using the same model. However, i would strongly recommend to think about it twice. In many cases, you will want to have a different view for edit operations(eg. hide some inputs that should not be editible) as well as the model could be slightly different, altought it might share some (or most) values. These difference will lead to some conditions in the view, checking whether you are creating or editing - which could make the code chaotic.
Conclusion: before deciding whether to have a shared view, try to think of how much is the edit screen gonna differ from create screen, then you may decide.
You certainly can, but usually that's something I will try to avoid. If the create and edit actions are virtually the same then you end up duplicating a lot of code in the controller. Usually in this situation I will have only a few fields on my 'Add' controller, and then once the item has been added I redirect the user to the edit page where they can fill in the rest of the information.
I wouldn't recommend that approach but you could have the main form be loaded into both views from a partial
[HttpGet]
public ActionResult myFun(int id = 0)
{
MyClass cls = new MyClass();
if (id == 0)
{
//Insert mode ... no data will be shown to textboxes , when primary key ie. id=0
//Display whole data
}
else
{
//Update mode... if id is not 0 ,data will be shown to textboxes
}
return View(cls);
}