MVC3 Custom view engine - c#

I have a custom View Engine that derives from WebFormViewEngine. There's a lot of stuff going on in here, mostly caching. I want to be able to use WebFormViewEngine AND RazorViewEngine at the same time, is this possible? Ideally I'd like to do;
ViewEngines.Add(new MyViewEngine<WebFormsViewEngine>());
ViewEngines.Add(new MyViewEngine<RazorViewEngine>());
if a .ascx/.aspx/.master file exists then use WebForms, otherwise use Razor is a .cshtml file exists.
EDIT: I should of worded my question better. As my custom view engine derives from WebFormViewEngine it obviously uses WebForms, I can't derive from two classes. I can derive from RazorViewEngine but then I'll loose WebForms. I can duplicate my code entirely, derive from RazorViewEngine and edit the views file extensions, etc. but as I said I've got a lot of custom code in my view engine and would be duplicating hundreds of lines.
WebFormViewEngine and RazorViewEngine derive from BuildManagerViewEngine which in turn implements IViewEngine. The problem with that is I have to implement methods CreatePartialView() and CreateView() but how would I know what to return (WebForms/Razor?) using generics?

It's possible, but not recommended way because it introduces some mess into your project. You can read good article on it: Using Multiple View Engines

It shouldn't matter.
By default, MVC3 includes both the WebFormsViewEngine and the RazorViewEngine, these are both registered automatically. I believe they are ordered as WebFormsViewEngine and then RazorViewEngine also, which means the web forms view will be checked first. If it finds a view, it will use it. If it cannot, it will use the next view engine, which is the Razor view engine.
Have you tried removing your custom view engine and just leaving the default configuration?

In the end I ended up with creating an abstract class: public abstract class MyViewEngine : BuildManagerViewEngine, IViewEngine { } and then implement/override the FindView and FindPartialView methods (which had my caching code in). I had an abstract method public abstract void SetSearchPaths(); that was called in my ctor.
I then created another class that derived from MyViewEngine for WebForms specifically:
public class MyViewEngineWebForms : MyViewEngine
{
public override void SetSearchPaths()
{
base.MasterLocationFormats = new string[] { "~/Skins/{2}/Views/{1}/{0}.master", "~/Skins/{2}/Views/Shared/{0}.master" };
base.ViewLocationFormats = new string[] { "~/Skins/{2}/Views/{1}/{0}.aspx", "~/Skins/{2}/Views/Shared/{0}.aspx", "~/Skins/Shared/Views/{0}.aspx" };
base.PartialViewLocationFormats = new string[] { "~/Skins/{2}/Views/{1}/{0}.ascx", "~/Skins/{2}/Views/Shared/{0}.ascx", "~/Skins/Shared/PartialViews/{0}.ascx" };
base.FileExtensions = new string[] { "aspx", "ascx", "master" };
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
return new WebFormView(controllerContext, partialPath, null, base.ViewPageActivator);
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
return new WebFormView(controllerContext, viewPath, masterPath, base.ViewPageActivator);
}
}
and did exactly the same for Razor but change the file extensions to .cshtml/.vbhtml and change WebFormView to RazorView. Add them to the ViewEngine collection:
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new SeeTicketsWebFormsViewEngine());
ViewEngines.Engines.Add(new SeeTicketsRazorViewEngine());
and now they work perfectly again, side by side with all of my custom caching logic.

Related

Inject object to the constructor of controller using unity container

I couldn’t find any similar issue so I’m writing this post. There is sample controller with private field IBaseClass. Sample code looks like follows:
public class TmpController : Controller
{
private IBaseClass _baseClass;
public TmpController()
{
_baseClass = new BaseClass(this);
}
}
public interface IBaseClass
{
//...
}
public class BaseClass : IBaseClass
{
protected TmpController TmpController;
public BaseClass(TmpController tmpController)
{
TmpController = tmpController;
}
//IBaseClass implementation
}
My question is; how to inject BaseClass object to the constructor of TmpController using Unity framework?
I want to make my controller "slimmer". I want to put the logic about validation and preparing dataSource of my controls like comboBox etc. to different class. I try to make some kind of SOC in my .Web project in that very specific case, which will make my controller easier to read and maintain. I'm using approach one controller per one view but I met the case with very complex form. Currently I have controller with more than 3000 lines of code and it's hard to maintain so I want to do something with it.
And yes I'm using Services and Repositories but the problem is about validation of ViewModels, mapping ViewModel objects into DTOs and backwards, preparing data source of given components etc.
#Razem, what you guess from my comment is correct. And the minus point you described is also valid.
What you are asking "Service depending on the controller" can surely be achieved but that would be a bad design.
Currently BaseClass is only dependent on TempController. How would you handle the scenario when you need the BaseClass in some other controller also? The code will start breaking and you will end up adding new dependency to BaseClass.
Also as per the design recommendations Top Layers should be dependent on the Bottom Layers not the vice versa.
Being said that, you can still achieve the feature you are looking for that too by making controller dependent on the IBaseClass.
I am not sure the specific reasons you need to access controller inside BaseClass. I have made certain assumptions while creating following suggestions. One of such assumption is BaseClass, IBaseClass and Controller classes are part of the same assembly.
//Have a BaseController Class with the properties and/or method which you will be using in the `BaseClass` class and make them virtual so that derived controller classes can override them to have specific implementation.
public class BaseController : Controller
{
public virtual string ControllerMethod()
{
return "Controller Method from Base Controller";
}
public virtual string RandomValue
{
get
{
return "Random value from Base Controller";
}
}
}
Create a method in IBaseClass which will Set the Controller for it.
public interface IBaseClass
{
void SetController(BaseController controller);
void Method1();
}
public class BaseClass : IBaseClass
{
private BaseController controller;
public void SetController(BaseController controller)
{
this.controller = controller;
}
public void Method1()
{
var str = this.controller.RandomValue;
}
}
And derive the TempController from the BaseController and make it dependent on IBaseClass. And in the constructor of TempController call SetController method of IBaseClass by passing this argument to it. You also can override method/properties of BaseController here.
After this you can call any method of IBaseClass without passing controller instance to it.
public class TempController : BaseController
{
private IBaseClass baseClass;
public HomeController(IBaseClass productService)
{
this.baseClass = productService;
this.baseClass.SetController(this);
}
public override string RandomValue
{
get
{
return "Random value from Derived Class.";
}
}
public ActionResult Index()
{
this.baseClass.Method1();
ViewBag.Title = "Home Page";
return View();
}
}
Install nuget package Unit.Mvc in your web project. Open file Unity.Config located under App_Start folder and change method RegisterTypes as following.
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<IBaseClass, BaseClass>(new PerRequestLifetimeManager());
}
I am sure I don't need to explain how this is going to work.
P.S. : You need to make sure that you calls IBaseClass.SetController method in controller constructor to avoid NullReferenceException when you use controller in BaseClass. This is small overhead you need to take to achieve good and maintainable design.

Programming to Interfaces and using loose coupling - studing TDD, unit testing and DI

Greetins,
Lets suppose I have a class called Formater:IFormater, and a method LoadData(), like this:
public interface IFormater { void LoadData(string strSomeData); }
public class Formater : IFormater { public void LoadData(string strSomeData) {/*do some magic*/} }
Suppose also I have a class called View:IView and a method LoadRawData(), like this:
public interface IView { void LoadRawData(string strSomeData); }
public class View : IView {
private IFormater _formater;
public View(IFormater formater) { _formater = formater; }
public void LoadRawData(string strSomeData) { _formater.LoadData(strSomeData); }
}
Now, in my prog I create an object of the View class and call LoadRawData(), something like this:
static void Main(string[] args) {
kernel = new StandardKernel(new Modules()); //ninject
formater = kernel.Get<IFormater>(); //ninject
IView view = new View(formater);
view.LoadRawData(args[0]);
}
I use ninject as DI mechanism and actually everything works fine now.
The questions:
Is it a correct to have the IView interface to call Formater::LoadDtata() internally? (For myself I don't see any other way).
Should I have Different names for Formater::LoadData() and View::LoadRawData(), or they should have the same name, or maybe there is a practice how to name them?
I'm sorry. It looks like I made several serius inaccuracies. I have corrected them above.
Actually I don't use MVC pattern. I only thought so.
The point is that I want to isolate display logic from parsing and formating it.
My console app should open a file, read text contents and pass it to formater class, that will do some formating and bring it back (but before that formater will pass it to parser to extract necessary contents from a raw text), like this
view <-> formater <-> parser
This is simple prog and I use it to better understand some best methodologies and practicies like TDD, unit testing and DI.
I'm sorry for previous inconsistiencis.
To answer question 1), as you are using the MVC pattern, your concrete view (not IView which is an interface) should not call IController::LoadData(). The relationship between the Model, View, Controller looks like:
(Image taken from this article which gives a basic definition of MVC)
Instead the controller should place the LoadData data into a model, and then the controller should return a view which is associated with (i.e. can access) this model.
e.g in ASP.NET MVC you could do something like:
public class YourController : Controller
{
IDataRepository db;
public YourController(IDataRepository db)
{
this.db = db;
}
public ViewResult Index()
{
YourModel model = db.LoadData();
return View(model);
}
}
What is not helping you is the fact that you are trying to use the MVC pattern from a console application. I'd suggest working through an MVC tutorial (e.g. this is pretty good) to get a better understanding.

Inheritance/Generics Basics. How to implement a code structure that's DRY

In an attempt to DRY up my code today i'd like to do the following. (I don't know if its the best way, but it seems better than to have an ever increasing code base where I continually need to update multiple methods if i want to change something across the whole site)
What i know about Inheritance is scary. As Iv'e never questioned any of the code/libraries that I use, and Iv'e never really attempted writing anything like this before, but I want to learn... Hoping this will be my day of enlightenment :P
To my question:
Say Iv'e got an add method (in all my controllers) like this:
public ActionResult Add(VM_Down_Time_Capture viewModel)
{
using (Down_Time_CaptureRepository repository = new Down_Time_CaptureRepository())
{
if (!ModelState.IsValid)
return ReturnValidationFailure(ViewData.ModelState.Values);
Down_Time_Capture model = new Down_Time_Capture();
model.InjectFrom(viewModel);
string mserMsg = repository.Add(model, User.Identity.Name);
if (!string.IsNullOrEmpty(mserMsg))
return ReturnCustomValidationFailure(Server.HtmlEncode(mserMsg));
repository.Save();
return Json("Added successfully.", JsonRequestBehavior.AllowGet);
}
}
And at the moment I've got the following as well.
Generated by T4 Templates/EF.
ViewModels, Repositories, (Standard) EF Models
I'm thinking I need a ModelSpecfic base controller for each page (can be done using T4), that inherits from a custom ControllerBase class that contains the basic CRUD functionality. That way i can have custom code per controller, and my code base will be cleaner & smaller & that wont get affected should i need to regenerate the base files
I don't quite understand how to implement something in the lines of what i need. What i understand so far is that ill need to have my repositories, and view models inherit from a base as well and somehow specify in [B] which ones I'm using... but as to how to do that i don't know
For example (and this is my best attempt at it, not my actual code, extremely hacky as I'm amazingly confused :S)
public class Down_Time_CaptureController : Down_Time_CaptureBase
{
//[A]
}
//Generated by T4
public class Down_Time_CaptureBase: ControllerBase
{
//[B]
public override EntityObject CreateNewModel()
{
return new Down_Time_Capture();
}
public override Base_Repository CreateNewRepository()
{
return new Down_Time_CaptureRepository();
}
public override Base_ViewModel CreateNewViewModel()
{
return new VM_Down_Time_Capture();
}
//how would i go about specifying which repository & model & view model to use
//although i expect it to be something to what i did here above
//and how would i go about calling the new generic add method (but in context of this controller)?
}
//coded once
public abstract class ControllerBase: Controller
{
//[C]
//make abstract so i have to override it
public abstract Base_Controller CreateNewModel();
public abstract Base_Controller CreateNewRepository();
public abstract Base_Controller CreateNewViewModel();
//I'm assuming my generified add method would go in here
public virtual ActionResult Add(Base_ViewModel viewModel)
{
using (Base_Repository repository = CreateRepository())
{
if (!ModelState.IsValid)
return ReturnValidationFailure(ViewData.ModelState.Values);
EntityObject model = CreateNewModel();
model.InjectFrom(viewModel);
string mserMsg = repository.Add(model, User.Identity.Name);
if (!string.IsNullOrEmpty(mserMsg))
return ReturnCustomValidationFailure(Server.HtmlEncode(mserMsg));
repository.Save();
return Json("Added successfully.", JsonRequestBehavior.AllowGet);
}
}
}
Here's a simple generic interpretation of what you are asking for:
// concrete controller implementation
public class Down_Time_CaptureController: ControllerBase<Down_Time_Capture, VM_Down_Time_Capture, Down_Time_CaptureRepository>
{
}
// generic controller base
public abstract class ControllerBase<TModel, TViewModel, TRepository>: Controller
where TModel : Base_Model, new()
where TViewModel : Base_ViewModel, new()
where TRepository : Base_Repository, new()
{
protected virtual TModel CreateNewModel()
{
return (TModel)Activator.CreateInstance<TModel>();
}
protected virtual TRepository CreateNewRepository()
{
return (TRepository)Activator.CreateInstance<TRepository>();
}
protected virtual TViewModel CreateNewViewModel()
{
return (TViewModel)Activator.CreateInstance<TViewModel>();
}
//I'm assuming my generified add method would go in here
public virtual ActionResult Add(TViewModel viewModel)
{
using (var repository = CreateRepository())
{
if (!ModelState.IsValid)
return ReturnValidationFailure(ViewData.ModelState.Values);
var model = CreateNewModel();
model.InjectFrom(viewModel);
string mserMsg = repository.Add(model, User.Identity.Name);
if (!string.IsNullOrEmpty(mserMsg))
return ReturnCustomValidationFailure(Server.HtmlEncode(mserMsg));
repository.Save();
return Json("Added successfully.", JsonRequestBehavior.AllowGet);
}
}
}
A few notes:
You will probably want to create interfaces for the three types (Model, ViewModel, Repository) and use those as the generic constraints.
You will probably want a generic Repository interface and base implementation (so you don't have to code each repository independently, and copy similar logic from one to the other).
Consider using an Inversion of Control container and dependency injection. Rather than have the controller, for example, handle creating an instance of a repository, make it a property and set it from the constructor. You can then use an IoC of your choice (like Ninject or Autofac) and register concrete implementations, and it will manage creating and the lifetime of both the dependencies and the controller itself.

Property Injection into an Action Filter

I'm trying to get Property Injection working on a Custom Action Filter Attribute. It is working as it is supposed to, however, I'd like to use DI on the Property itself. My filter looks like this
[AttributeUsage(AttributeTargets.Class)]
public sealed class HeaderFilterAttribute : ActionFilterAttribute
{
public IMarketService MarketService
{ get; set; }
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var view = (ViewResultBase)filterContext.Result;
if (view != null)
{
BaseViewModel viewModel = view.ViewData.Model as BaseViewModel;
if (viewModel != null)
viewModel.Header = GetHeaderScript();
}
base.OnActionExecuted(filterContext);
}
private string GetHeaderScript()
{
//Use MarketService here and return header script
return "script";
}
}
This is how I'm configuring the property using StructureMap inside my BootStrapper class.
//HeaderFilterAttribute
IMarketRepository marketRepository = new SqlMarketRepository();
IMarketService marketService = new MarketService(marketRepository);
ObjectFactory.Container.Configure(r => r.ForConcreteType<HeaderFilterAttribute>().
Configure.WithProperty("MarketService").
EqualTo(marketService));
My problem is I do not have access to SqlMarketRepository since all my concrete types are injected via DI and I really don't want to use concrete types in my bootstrapper. So the ultimate question now is, how do I inject MarketService into the Filter attribute without resorting to the above? :)
In your ObjectFactory.Initialize() call, add the following line:
SetAllProperties(x => x.OfType<IMarketService>());
That will inject the configured IMarketService instance into any property of type IMarketService, on any object retrieved from the container.
I think you need a custom action invoker implementation that will resolve the filters. You can dig a Windsor sample out of my company's implementation (about 1/2 way down). There should be several more available online. I know I've seen some on this site.
PS. I noticed you're using a base view model to populate a header. I'd recommend using the ViewData[] collection with a static key instead of inheritance in your view model. :)

Render partial from different folder (not shared)

How can I have a view render a partial (user control) from a different folder?
With preview 3 I used to call RenderUserControl with the complete path, but whith upgrading to preview 5 this is not possible anymore.
Instead we got the RenderPartial method, but it's not offering me the functionality I'm looking for.
Just include the path to the view, with the file extension.
Razor:
#Html.Partial("~/Views/AnotherFolder/Messages.cshtml", ViewData.Model.Successes)
ASP.NET engine:
<% Html.RenderPartial("~/Views/AnotherFolder/Messages.ascx", ViewData.Model.Successes); %>
If that isn't your issue, could you please include your code that used to work with the RenderUserControl?
In my case I was using MvcMailer (https://github.com/smsohan/MvcMailer) and wanted to access a partial view from another folder, that wasn't in "Shared." The above solutions didn't work, but using a relative path did.
#Html.Partial("../MyViewFolder/Partials/_PartialView", Model.MyObject)
If you are using this other path a lot of the time you can fix this permanently without having to specify the path all of the time. By default, it is checking for partial views in the View folder and in the Shared folder. But say you want to add one.
Add a class to your Models folder:
public class NewViewEngine : RazorViewEngine {
private static readonly string[] NEW_PARTIAL_VIEW_FORMATS = new[] {
"~/Views/Foo/{0}.cshtml",
"~/Views/Shared/Bar/{0}.cshtml"
};
public NewViewEngine() {
// Keep existing locations in sync
base.PartialViewLocationFormats = base.PartialViewLocationFormats.Union(NEW_PARTIAL_VIEW_FORMATS).ToArray();
}
}
Then in your Global.asax.cs file, add the following line:
ViewEngines.Engines.Add(new NewViewEngine());
For readers using ASP.NET Core 2.1 or later and wanting to use Partial Tag Helper syntax, try this:
<partial name="~/Views/Folder/_PartialName.cshtml" />
The tilde (~) is optional.
The information at https://learn.microsoft.com/en-us/aspnet/core/mvc/views/partial?view=aspnetcore-3.1#partial-tag-helper is helpful too.
For a user control named myPartial.ascx located at Views/Account folder write like this:
<%Html.RenderPartial("~/Views/Account/myPartial.ascx");%>
I've created a workaround that seems to be working pretty well. I found the need to switch to the context of a different controller for action name lookup, view lookup, etc. To implement this, I created a new extension method for HtmlHelper:
public static IDisposable ControllerContextRegion(
this HtmlHelper html,
string controllerName)
{
return new ControllerContextRegion(html.ViewContext.RouteData, controllerName);
}
ControllerContextRegion is defined as:
internal class ControllerContextRegion : IDisposable
{
private readonly RouteData routeData;
private readonly string previousControllerName;
public ControllerContextRegion(RouteData routeData, string controllerName)
{
this.routeData = routeData;
this.previousControllerName = routeData.GetRequiredString("controller");
this.SetControllerName(controllerName);
}
public void Dispose()
{
this.SetControllerName(this.previousControllerName);
}
private void SetControllerName(string controllerName)
{
this.routeData.Values["controller"] = controllerName;
}
}
The way this is used within a view is as follows:
#using (Html.ControllerContextRegion("Foo")) {
// Html.Action, Html.Partial, etc. now looks things up as though
// FooController was our controller.
}
There may be unwanted side effects for this if your code requires the controller route component to not change, but in our code so far, there doesn't seem to be any negatives to this approach.
The VirtualPathProviderViewEngine, on which the WebFormsViewEngine is based, is supposed to support the "~" and "/" characters at the front of the path so your examples above should work.
I noticed your examples use the path "~/Account/myPartial.ascx", but you mentioned that your user control is in the Views/Account folder. Have you tried
<%Html.RenderPartial("~/Views/Account/myPartial.ascx");%>
or is that just a typo in your question?
you should try this
~/Views/Shared/parts/UMFview.ascx
place the ~/Views/ before your code
Create a Custom View Engine and have a method that returns a ViewEngineResult
In this example you just overwrite the _options.ViewLocationFormats and add your folder directory
:
public ViewEngineResult FindView(ActionContext context, string viewName, bool isMainPage)
{
var controllerName = context.GetNormalizedRouteValue(CONTROLLER_KEY);
var areaName = context.GetNormalizedRouteValue(AREA_KEY);
var checkedLocations = new List<string>();
foreach (var location in _options.ViewLocationFormats)
{
var view = string.Format(location, viewName, controllerName);
if (File.Exists(view))
{
return ViewEngineResult.Found("Default", new View(view, _ViewRendering));
}
checkedLocations.Add(view);
}
return ViewEngineResult.NotFound(viewName, checkedLocations);
}
Example: https://github.com/AspNetMonsters/pugzor
Try using RenderAction("myPartial","Account");

Categories