The default MVC template uses "#DateTime.Now.Year" to display the copyright year, but I'd much rather use NodaTime everywhere.
I'm currently using Ninject to inject an instance of IClock into Controllers that do time or date specific stuff. Is there a recommended way to access a "global IClock" in MVC similar to the "DateTime.Now"? I suppose I could inject the IClock into every Controller then pass it into every view, but it would be nice to have access to something global sometimes.
I know I could use SystemClock.Instance in the Layout template... it would be much nicer to reference a global, testable IClock instead.
You could use a child action.
Start by writing a controller in which you could use dependency injection as usual and which will contain a child action:
public class CopyrightController: Controller
{
private readonly IClock clock;
public CopyrightController(IClock clock)
{
this.clock = clock;
}
[ChildActionOnly]
public ActionResult Index()
{
// In this example I am directly passing the IClock instance
// to the partial view as model but in a real application
// you might want to use a view model here
return PartialView(this.clock);
}
}
and then you could have a corresponding partial view (~/Views/Copyright/Index.cshtml):
#model IClock
<div>Copyright ...</div>
and finally in your _Layout call this child action:
<footer>
#Html.Action("Copyright", "Index")
</footer>
If you're looking for a testable DateTime.Now, you could use SystemTime.Now(), as detailed in this post by Ayende Rahien. You can create a Func<DateTime> that defaults to DateTime.Now but that you can set to a specific date in your tests. It saves you introducing another dependency. Here's the func:
public static class SystemTime
{
public static Func<DateTime> Now = () => DateTime.Now;
}
Wherever you would use DateTime.Now you should use SystemTime.Now instead. You can set it in your tests like this:
SystemTime.Now = () => new DateTime(2012, 7, 1).Date;
Provided you have adequate test coverage of your usage of dates, you should catch any instances in which you are using DateTime.Now instead of SystemTime.Now
Related
I'm currently switching from .net framework to .net core 3.1.
Defining Api Controllers inside the namespace is all fine and works.
Now I have the case, that I need to declare the Api Controllers within another class, like this:
namespace Api.Controllers
{
public class MainClass : BaseClass
{
public MainClass()
{
}
[ApiController]
[Route("Test")]
public class TestController : ControllerBase
{
[HttpGet]
public int GetResult()
{
return 0;
}
}
}
}
The result is, that the Api Controller can't be found after calling the "AddControllers" method inside the "ConfigureServices" method of the startup class.
The MainClass is instantiated before the Startup class will be called.
I've tried to change the global route and defining an area like "{area=Test}/{controller=Test}", or set the ApiController attribute above the MainClass, but none of them worked.
Is there a way to get this working?
Looks like the default ControllerFeatureProvider does not treat nested controller types as controller. You can add (don't need to replace) your custom provider to change that behavior, like this:
public class NestedControllerFeatureProvider : ControllerFeatureProvider
{
protected override bool IsController(TypeInfo typeInfo)
{
if(!typeInfo.IsClass) return false;
if(typeInfo.IsAbstract) return false;
var isNestedType = typeInfo.DeclaringType != null;
var isPublic = true;
var parentType = typeInfo.DeclaringType;
while(parentType != null){
isPublic = parentType.IsPublic;
parentType = parentType.DeclaringType;
}
return isNestedType && isPublic;
}
}
Then add that provider to the ApplicationPartManager in Startup.ConfigureServices like this:
services
.AddMvc()
.ConfigureApplicationPartManager(appPart => {
appPart.FeatureProviders.Add(new NestedControllerFeatureProvider());
});
If you want to replace the default ControllerFeatureProvider, just find it in the FeatureProviders and remove it. Of course then you need to ensure that your custom one should handle everything just like what done by the default logic, something like this:
//for IsController
return base.IsController(typeInfo) || <...your-custom-logic...>;
NOTE: You can refer to the default implementation of ControllerFeatureProvider to learn some standard logic to implement your own logic correctly. The code above is just a basic example. To me, as long as the classes inherits from ControllerBase and not abstract, they can work fine as a controller to serve requests. There would be no serious troubles except some weird conventions (e.g: class name not ending with Controller is still a controller or some standard attributes applied on the controller class are not working ...).
We should not use nested controller classes. Each controller class should be put in a separate file (as a good practice). However the point of this answer (the most interesting part that I'm pretty sure not many know about, is the use of ControllerFeatureProvider which can help you customize the features set in other scenarios). And really if you really have to stick with your design somehow, you of course have to use this solution, no other way.
I'm investigating how to use OData in ASP.Core.
I've created a BooksController, subclassed from ODataController, within which I've defined two Actions: Get(), and Get(int id).
/odata/books resolves to the first Action, but /odata/books(1) does not find the second Action.
Once the Models are defined, it is able to find the following Controller:
[ODataRoutePrefix("Books")]
public class BooksController : ODataController
{
private BookStoreContext _db;
public BooksController(BookStoreContext context)
{
_db = context;
}
[ODataRoute]
[EnableQuery]
public IActionResult Get()
{
return Ok(_db.Books);
}
[EnableQuery]
[ODataRoute("({key})")]
public IActionResult Get([FromODataUri] int key)
{
return Ok(_db.Books.FirstOrDefault(c => c.Id == key.ToGuid()));
}
}
The site has the default Convention rules for all routes (see below).
But I think this is not in play, as the BooksController is decorated with [ODataRoutePrefix("Books")] and the actions with [ODataRoute] (and [EnableQuery]) -- which I think, being Attribute based routing, take precedence (is that a correct assumption?).
My dto models are are registered using Reflection...), but the key part is where Startup invokes UseMvc(...), and defines the routes, which ends up calling here:
private void CreateODataRoutes(IRouteBuilder routeBuilder)
{
// register the convention routes for MVC first...
routeBuilder.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
...
// then do the OData stuff...
routeBuilder.Count().Expand().Filter()
.MaxTop(100).OrderBy().Select();
// Use method further down the page
// to create a Build Model by reflection, using
// all OData Model definitions (ie, classes that implement
// IAllModulesOdataModelBuilderConfiguration)
var oDataConventionModelBuilder = BuildODataModelUsingReflectionAcrossAllAssemblies();
// Use the modelBuilder as the basis of defining routes:
RegisterRoutesToODataController(routeBuilder, oDataConventionModelBuilder);
}
Where BuildODataModelUsingReflectionAcrossAllAssemblies uses reflection to find individual model definitions, each one pretty simple, only defining their id (relying on convention for the rest).
Note that I'm not defining Actions as that used to be by convention (see further down).
public class BookODataModelBuilderConfigurationBase<T> : IAllModulesOdataModelBuilderConfiguration
where T : class, IHasGuidId, new()
{
public virtual void Apply(ODataModelBuilder builder ...)
{
var _controllerName = this.GetControllerNameByConvention(typeof(Book));
var entity = builder.EntitySet<T>(this._controllerName).EntityType;
entity.HasKey(x => x.Id);
//Note...no Actions defined, as planning to rely on default conventions (routing by Verb to method starting with Get...)
}
}
When the model is created, it is registered as follows;
private void RegisterRoutesToODataController(IRouteBuilder routeBuilder,
ODataConventionModelBuilder oDataConventionModelBuilder)
{
string routePrefix = $"{App.Modules.Core.Shared.Constants.ModuleSpecific.Module.AssemblyNamePrefix}.";
// Build the Edm model used to parse commands:
var edmModel = oDataConventionModelBuilder.GetEdmModel();
// Register the Odata paths
routeBuilder.MapODataServiceRoute(
routeName: $"{routePrefix}odataDefault",
routePrefix: "odata",
edmModel,
pathHandler:new DefaultODataPathHandler(),
// By convention? So that Get verb goes to Get action, etc.
routingConventions: ODataRoutingConventions.CreateDefault()
);
}
When the path is /odata/book(1) it returns HTTP ERROR 404, the page does not exist.
Thank you!
Other things I've tried include:
commented out configuration of SwaggerAPI
Removed [FromODataUri] on the key param (is it necessary?)
Added/removed [ODataRoute("({key})")]
Registered the Controller as BooksController in plural/singular
Changed the name of the Action to GetBook and back again to Get
Added/removed ODataRoutePrefix
registering the OData routes before registering the default convention routes (think that should be the case all the time, right?).
...all of which is starting to look more like desperation than coding :-( ...
Still looking. Thanks for any guidance.
OMG. (sheepishly) solved.
It wasn't the Framework, Nuget, the Controller base class, routeprefix, routes, or anything glorious, it was ...me.
The only place I was not looking was the Model itself, which defined the Id as a Guid. The Action was using an int, converting it to a Guid.
ASP.Core could not find it because it was building routes based on the Model (not the Controller), so ignored the Action as it made no sense to its convention based route building as an int != Guid. duh.
If you were wondering why the heck I used an int...it was because when I had seeded the Db I wanted a Guid Key, but for testing purposes I wanted some records to have specific Ids that I could refer back to, and I was lazy, and didn't want to type in a full Guid.
That was a DUMB idea, in retrospect...:-(
But thank you for looking into it! Appreciate the time spent.
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.
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>();
Looking for some guidance in designing my new MVC 4 app.
I would like to have a url parameter s=2011 on every page of the app to let me know what year of data I'm working with. Obviously, the user will have a way to change that parameter as needed.
I will need that parameter in every controller and wondering the best way to do this. I was thinking of creating a base controller that reads Request.QueryString and puts the year into a public property. However, considering all the extensability points in MVC, I'm wondering if there's a better way to do this?
This very much depends on the design of your app, but just to give you two alternatives
IActionFilter
If you are doing data context per request you can use a global IActionFilter to hook pre-action execution globally and apply a query filter to your data context behind the scenes.
Major down-side of this is that to test the controller you will need to have the full MVC pipeline setup so that the actionfilter gets applied properly.
Dependency Injection
Instead of using sub-classing (base controller as you say) you can use dependency injection . Keeping things more loose will allow you to pull the filter from query string, cookie, user setting in the database or whatever else - without your controller knowing where it comes from.
Here is some pseudo code how I would do it if I was using something like Entity Framework or Nhibernate (also I am sure applicable with other technologies as well)
public Car
{
public string Year { get; set; }
}
public class CarsDataContext : DbContext
{
private IQuerable<Cars> _cars = null;
private Func<Car, bool> _carsFilter = null;
public IQuerable<Car> Cars {
get {
if (_carsFitler != null)
return _cars.Where(_carsFitler);
return _cars;
}
set { _cars = value; }
}
public void ApplyCarsFilter(Func<Car, bool> predicate)
{
_carsFilter = predicate;
}
}
Assuming you have dependency injection setup already (NInject or whichever other framework) in you can configure how the context to be intialized
Bind<CarsDataContext>().ToMethod(() => {
string yearFilter = GetYearFilter(); // can be coming from anywhere
CarsDataContext dataContext = new CarsDataContext();
dataContext.Applyfilter(car => car.Year == yearFilter);
return dataContext;
}).InRequestScope();
Then my controller knows nothing about the data filtering and I can easily test it:
class MyController : Controller
{
public MyController(CarsDataContext dataContext)
{
}
...
}
However I would only do this is filtering the dataset was across many controllers and important part of my software. Otherwise it's pure over-engineering.