I'm just going through some intro tutorials for ASP.NET and I've got a decent idea of how to implement a simple CRUD admin app.
Are there any commonly used patterns to implement generic List/Create/Update/Delete actions? It seems pretty tedious to have to build scaffolding for every model, and then to maintain all of the add, edit and list views and controllers. It would be a lot more efficient and less error-prone to implement generic actions like:
/List/Model
/Edit/Model/id
/Update/Model/id
/Delete/Model/id
that would handle any model.
I've done something similar, I think, to what you're talking about in an admin application I built. Basically, the key is to use generics. In other words, you create a controller like:
public abstract class AdminController<TEntity> : Controller
where TEntity : IEntity, class, new()
{
protected readonly ApplicationDbContext context;
public virtual ActionResult Index()
{
var entities = context.Set<TEntity>()
return View(entities);
}
public virtual ActionResult Create()
{
var entity = new TEntity();
return View(entity);
}
[HttpPost]
public virtual ActionResult Create(TEntity entity)
{
if (ModelState.IsValid)
{
context.Set<TEntity>().Add(entity);
context.SaveChanges();
return RedirectToAction("Index");
}
return View(entity);
}
...
}
In other words, you just build an entire reusable controller structure, with the key parts being that you're using the generic TEntity instead of a concrete class. Notice that TEntity is defined as IEntity, class, new(). This does a few things. First, class allows you to treat it as a concrete type and new() means that the type will be something that can be instantiated, rather than something like an abstract class. IEntity is just a placeholder for whatever you may be using in your application to ensure all the types have some common denominator. At the very least for a CRUD-style application, you'll need this to gain access to an Id or similar property for things like your edit and delete actions. Saying that TEntity implements IEntity lets you utilize any properties on IEntity. If you use a concrete type here instead of an interface, you can leave off the class part, e.g. where TEntity : Entity, new().
Then, in order to use this, you just define a new controller that inherits from AdminController<> and specify the type you're working with:
public class WidgetController : AdminController<Widget>
{
public WidgetController(ApplicationDbContext context)
{
this.context = context;
}
}
That could be potentially all you need for your individual controllers. Also, worth noting here is that I've set this up to employ dependency injection for your context. You could always change your constructor to something like:
public WidgetController()
{
this.context = new ApplicationDbContext();
}
But, I recommend you do look into using dependency injection, in general. Also, I'm using the context directly here for ease of explanation, but usually you'd be employing services, repositories, etc. here instead.
Finally, if you find you need to customize certain parts of a CRUD action, but not necessarily the whole thing, you can always add methods as extension points. For example, let's say you needed to populate a select list for one particular entity, you might do something like:
public abstract class AdminController<TEntity> : Controller
where TEntity : IEntity, class, new()
{
...
public virtual ActionResult Create()
{
var entity = new TEntity();
BeforeReturnView();
return View(entity);
}
...
protected virtual void BeforeReturnView()
{
}
...
And then:
public class WidgetController : AdminController<Widget>
{
...
protected override void BeforeReturnView()
{
ViewBag.MySelectList = new List<SelectListItem>
{
...
};
}
}
In other words, you have a hook in your base action method that you override to just change that particular bit of functionality instead of having to override the whole action itself.
You can also take this farther to include things like view models, where you might expand your generic class definition to something like:
public abstract class AdminController<TEntity, TEntityViewModel, TEntityCreateViewModel, TEntityUpdateViewModel>
where TEntity : IEntity, class, new()
where TEntityViewModel : class, new()
...
And then:
public class WidgetController : AdminController<Widget, WidgetViewModel, WidgetCreateViewModel, WidgetUpdateViewModel>
{
...
}
It all depends on what your application needs.
Related
I have a service layer with the following classes / intefaces (IServices is an empty interface):
public interface IForoChanService<T> : IService
{
T GetById(int id);
IQueryable SearchBy(Expression<Func<T, bool>> predicate);
IEnumerable<T> GetAll();
int Create(T entity);
void CreateMany(IEnumerable<T> entities);
void Delete(T entity);
void Delete(int id);
void DeleteMany(IEnumerable<T> entities);
void Update(T entity);
}
Then I have an abstract class implementing that signature generically:
public abstract class ForoChanServiceBase<T> : IForoChanService<T> where T : EntityBase
{
public T GetById(int id)
{
return ChanDbContext.Set<T>().Find(id);
}
//all the other methods as well
}
And finally the concrete classes:
public class CategoryService : ForoChanServiceBase<Category>
{
}
I am trying to use AutoFac to inject those services (many: category, client, etc) in the constructor: I have a base controller:
public abstract class ForoChanBaseController: Controller
{
protected ForoChanServiceBase<Post> PostService { get; private set; }
protected ForoChanServiceBase<Comment> CommentService { get; private set; }
protected ForoChanServiceBase<Category> CategoryService { get; private set; }
protected ForoChanBaseController()
{
}
protected ForoChanBaseController(
ForoChanServiceBase<Post> postService,
ForoChanServiceBase<Comment> commentService,
ForoChanServiceBase<Category> categoryService)
{
PostService = postService;
CommentService = commentService;
CategoryService = categoryService;
}
}
And I am setting autofac like this:
public static void ConfigureIoc()
{
var builder = new ContainerBuilder();
builder.RegisterType<CommentService>().As<ForoChanServiceBase<Comment>>().InstancePerRequest();
builder.RegisterType<CategoryService>().As<ForoChanServiceBase<Category>>().InstancePerRequest();
builder.RegisterType<PostService>().As<ForoChanServiceBase<Post>>().InstancePerRequest();
builder.Build();
}
The problem is that I am having is when in the controller I need to use any service method that guy (CategoryService) is null:
public ActionResult Create()
{
var p = new PostFormNewVm
{
Categories = CategoryService.GetAll().Select(c => new CategoryVm { Id = c.Id, Title = c.Title })
};
return View(p);
}
Besides this error do am I doing something wrong? I can't make it work.
I tried with the inteface as well.
Your ForoChanBaseController contains multiple constructors, which is an anti-pattern. Because of the existence of this default constructor, there is a derived class that uses this constructor instead of the overloaded one, which is causing the dependencies to be null.
Although this default ctor is the cause for you to post the question here, there are more -less obvious problems- with your design:
Although you can remove the default constructor, prevent having this base class at all. Bases classes are often big Single Responsibility Principle violations and are either used to stuff in cross-cutting concerns or other utility functions. By having this base class derived types are forced to require dependencies that they might not even use at all. This complicates your code and complicates testing.
Since you have the IForoChanService<T> interface, consumers should not depend on the ForoChanServiceBase base class. As a matter of fact, the same advise as before holds: this base class should probably not exist at all.
The IForoChanService<T> is big generic tool box of methods where consumers only use one or two of those methods at a time. This means you are violating the Interface Segregation Principle.
IForoChanService<T> implementations are likely to violate the Liskov Substitution Principle, since there will be implementations that don't allow entities to be deleted. This will cause call to Delete to fail with an exception, instead of the Delete to not exist for that entity.
I created a custom action filter to perform logging for auditing trails.I added my logging code to public override void OnActionExecuting(ActionExecutingContext filterContext).
My question is, how do I pass my EF dbContext to this method? I'd like to write a single action filter and re-use it on other development projects without changing the dbcontext for every project.
If this isn't a recommended practice, what should I do?
If I were doing such a thing, I think I would be implementing a generic audit logging method in the service layer that already is aware of the data context and pass on an audit model to it. This way, if there's ever a need to log different parts of the application (that may not even be related to controllers), you don't have to re-implement anything.
Alternatively if you want to stick to just controllers you could perhaps make an interface for the data context
public interface IDataContext<T> where T : DbContext
{
T DataContext { get; }
}
Create a BaseController that implements in along with System.Web.Mvc.Controller
public class BaseController : Controller, IDataContext<YourDbContextClass>
{
public YourDbContextClass DataContext { get { return new YourDbContextClass(); } }
}
You can use this base class on controllers and reach the context via DataContext, but for logging, you can create a new class with your overridden method
public class AuditController : BaseController
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// whatever you do inside here
// DataContext.LaDiDa
}
}
And then inherit your stuff from it
public class HomeController : AuditController
{
public ActionResult Index()
{
return View();
}
}
Although this isn't the most straight forward solution, you could lose the interface all together and do everything in the BaseController, but I'm just throwing stuff on the board in case you see something that would work for you.
I need to setup a policy in base controller that applies to all controller instance, like below:
public class BaseController : Controller
{
private IPolicy Policy;
public BaseController()
{
this.Policy= new Policy(HttpContext);
}
}
Within the Policy class, I need to do something like:
this.httpContextBase.User.
Questions: (Update)
What is the better way to design the BaseController in terms of using HttpContext and Unit test.
What is the correct way to unit test HttpContext?
Absolutely no way. You are using the HttpContext inside the constructor of a controller when this context is still not initialized. Not only that this code cannot be tested but when you run the application it will also crash with NRE. You should never use any HttpContext related stuff in a constructor of a controller.
One possibility is to refactor your code and perform this inside the Initialize method:
public class BaseController : Controller
{
private IPolicy Policy;
protected override void Initialize(RequestContext requestContext)
{
base.Initialize(requestContext);
this.Policy = new Policy(HttpContext);
}
}
This being said, that's not the approach I would recommend. I would recommend you using dependency injection instead of service location which is considered by many as an anti-pattern.
So:
public abstract class BaseController : Controller
{
protected IPolicy Policy { get; private set; }
protected BaseController(IPolicy policy)
{
Policy = policy;
}
}
Now, all that's left is to configure your favourite Dependency Injection framework to inject the correct instance into the constructor. For example with Ninject.Mvc3 this is achieved with a single line of code:
kernel.Bind<IPolicy>().To<Policy>();
Now you can feel more than free to mock this IPolicy in your unit test without even caring about any HttpContext.
For example let's suppose that you have the following controller that you want to unit test:
public class FooController : BaseController
{
public FooController(IPolicy policy): base(policy)
{ }
[Authorize]
public ActionResult Index()
{
Policy.DoSomething();
return View();
}
}
Now, all that you need to do is pick up your favorite mock framework (Rhino Mocks in my case) and do the mocking:
[TestMethod]
public void Index_Action_Should_DoSomething_With_The_Policy()
{
// arrange
var policyStub = MockRepository.GenerateStub<IPolicy>();
var sut = new FooController(policyStub);
// act
var actual = sut.Index();
// assert
Assert.IsInstanceOfType(actual, typeof(ViewResult));
policyStub.AssertWasCalled(x => x.DoSomething());
}
I have read many posts on Session-scoped data in MVC, but I am still unclear where is the right place to include a custom Session wrapper into the solution.
I want to get the Username of the current user from the IPrincipal, load additional information about that User and store it in the Session. Then I want to access that User data from the Controller and the View.
None of the following approaches seem to fit what I want to do.
Option 1 : Access the Session collection directly
Everyone seems to agree this is a bad idea, but honestly it seems like the simplest thing that works. However, it doesn't make the User available to the view.
public class ControllerBase : Controller {
public ControllerBase() : this(new UserRepository()) {}
public ControllerBase(IUserRepository userRepository) {
_userRepository = userRepository;
}
protected IUserRepository _userRepository = null;
protected const string _userSessionKey = "ControllerBase_UserSessionKey";
protected User {
get {
var user = HttpContext.Current.Session[_userSessionKey] as User;
if (user == null) {
var principal = this.HttpContext.User;
if (principal != null) {
user = _userRepository.LoadByName(principal.Identity.Name);
HttpContext.Current.Session[_userSessionKey] = user;
}
}
return user;
}
}
}
Option 2: Injecting the Session into the class constructor forum post
This option seems pretty good, but I am still not sure how to attach it to the Controller and the View. I could new-it-up in the Controller, but shouldn't it be injected as a dependency?
public class UserContext {
public UserContext()
: this(new HttpSessionStateWrapper(HttpContext.Current.Session),
new UserRepository()) { }
public UserContext(HttpSessionStateBase sessionWrapper, IUserRepository userRepository) {
Session = sessionWrapper;
UserRepository = userRepository;
}
private HttpSessionStateBase Session { get; set; }
private IUserRepository UserRepository{ get; set; }
public User Current {
get {
//see same code as option one
}
}
}
Option 3 : Use Brad Wilson's StatefulStorage class
In his presentation Brad Wilson features his StatefulStorage class. It is a clever and useful set of classes which include interfaces and uses constructor injection. However, it seems to lead me down the same path as Option 2. It uses interfaces, but I couldn't use the Container to inject it because it relies on a static factory. Even if I could inject it, how does it get passed to the View. Does every ViewModel have to have a base class with a setable User property?
Option 4 : Use something similar to the Hanselman IPrincipal ModelBinder
I could add the User as a parameter to the Action method and use a ModelBinder to hydrate it from the Session. This seems like a lot of overhead to add it everywhere it is needed. Plus I would still have to add it to the ViewModel to make it available to the View.
public ActionResult Edit(int id,
[ModelBinder(typeof(IPrincipalModelBinder))] IPrincipal user)
{ ... }
I feel like I am overthinking this, but it also seems like there should be an obvious place to do this sort of thing. What am I missing?
My approach to Session:
Cover Session with interface:
public interface ISessionWrapper
{
int SomeInteger { get; set; }
}
Implement interface using HttpContext.Current.Session:
public class HttpContextSessionWrapper : ISessionWrapper
{
private T GetFromSession<T>(string key)
{
return (T) HttpContext.Current.Session[key];
}
private void SetInSession(string key, object value)
{
HttpContext.Current.Session[key] = value;
}
public int SomeInteger
{
get { return GetFromSession<int>("SomeInteger"); }
set { SetInSession("SomeInteger", value); }
}
}
Inject into Controller:
public class BaseController : Controller
{
public ISessionWrapper SessionWrapper { get; set; }
public BaseController(ISessionWrapper sessionWrapper)
{
SessionWrapper = sessionWrapper;
}
}
Ninject dependency:
Bind<ISessionWrapper>().To<HttpContextSessionWrapper>()
You can pass some commonly used information using ViewData when you want to use it in master page and using view model in specific views.
I would strongly recommend passing anything you need in the view down via the controller. That way, the decision on exactly what data the view should render stays with the controller. In order to make that as easy as possible, creating an abstract ViewModelWithUserBase class that has a settable User property really isn't a bad idea. An option is to create an interface IViewModelWithUser, and re-implement the User property every time (or combine with the base class, but you would have the option to re-implement instead of inheriting the base class if that makes things easier in some corner cases).
As far as populating this property, it can probably be done easily with an action filter. Utilizing the OnActionExecuted method you can test if the model passed to the view implements your base class (or interface), and then fill the property with the correct IPrincipal object if appropriate. This has the advantage that since action filters aren't executed in unit tests, you can use the HttpContext.Current.Session dependent code from your option 1 in your action filter, and still have a testable interface on the controller.
I edited my whole question, so do not wonder :)
Well, I want to have an ActionResult that takes domain model data and some additional parameters, i.e page index and page size for paging a list. It decide itself if it returns a PartialViewResult or a ViewResult depending on the kind of web request (ajax request or not).
The reffered data shall be mapped automatically by using an IMappingService, which is responsible for transforming any domain model data into a view model.
The MappingService uses AutoMapper for simplicity.
MappingActionResult:
public abstract class MappingActionResult : ActionResult
{
public static IMappingService MappingService;
}
BaseHybridViewResult:
public abstract class BaseHybridViewResult : MappingActionResult
{
public const string defaultViewName = "Grid";
public string ViewNameForAjaxRequest { get; set; }
public object ViewModel { get; set; }
public override void ExecuteResult(ControllerContext context)
{
if (context == null) throw new ArgumentNullException("context");
var usePartial = ShouldUsePartial(context);
ActionResult res = GetInnerViewResult(usePartial);
res.ExecuteResult(context);
}
private ActionResult GetInnerViewResult(bool usePartial)
{
ViewDataDictionary viewDataDictionary = new ViewDataDictionary(ViewModel);
if (String.IsNullOrEmpty(ViewNameForAjaxRequest))
{
ViewNameForAjaxRequest = defaultViewName;
}
if (usePartial)
{
return new PartialViewResult { ViewData = viewDataDictionary, ViewName = ViewNameForAjaxRequest };
}
return new ViewResult { ViewData = viewDataDictionary };
}
private static bool ShouldUsePartial(ControllerContext context)
{
return context.HttpContext.Request.IsAjaxRequest();
}
}
AutoMappedHybridViewResult:
public class AutoMappedHybridViewResult<TSourceElement, TDestinationElement> : BaseHybridViewResult
{
public AutoMappedHybridViewResult(PagedList<TSourceElement> pagedList)
{
ViewModel = MappingService.MapToViewModelPagedList<TSourceElement, TDestinationElement>(pagedList);
}
public AutoMappedHybridViewResult(PagedList<TSourceElement> pagedList, string viewNameForAjaxRequest)
{
ViewNameForAjaxRequest = viewNameForAjaxRequest;
ViewModel = MappingService.MapToViewModelPagedList<TSourceElement, TDestinationElement>(pagedList);
}
public AutoMappedHybridViewResult(TSourceElement model)
{
ViewModel = MappingService.Map<TSourceElement, TDestinationElement>(model);
}
public AutoMappedHybridViewResult(TSourceElement model, string viewNameForAjaxRequest)
{
ViewNameForAjaxRequest = viewNameForAjaxRequest;
ViewModel = MappingService.Map<TSourceElement, TDestinationElement>(model);
}
}
Usage in controller:
public ActionResult Index(int page = 1)
{
return new AutoMappedHybridViewResult<TeamEmployee, TeamEmployeeForm>(_teamEmployeeRepository.GetPagedEmployees(page, PageSize));
}
So as you can see the IMappingService is hidden. The controller should not know anything about the IMappingService interface, when AutoMappedHybridViewResult is used.
Is the MappingActionResult with the static IMappingServer appropriate or am I violating the DI principle?
I think a better design is to have a ViewResultFactory that depends on IMappingService, then you can inject that into your controller. Then you call it like so:
public class MyController : Controller
{
IViewResultFactory _viewResultFactory;
ITeamEmployeeRepository _teamEmployeeRepository;
public MyController(IViewResultFactory viewResultFactory)
{
_viewResultFactory = viewResultFactory;
}
public ActionResult MyAction(int page, int pageSize)
{
return
_viewResultFactory.GetResult<TeamEmployee, TeamEmployeeForm>(
_teamEmployeeRepository.GetPagedEmployees(page, pageSize));
}
}
The implementation would like this (you would need to create overloads for each of your HybridViewResult constructors):
public HybridViewResult<TSourceElement, TDestinationElement> GetResult<TSourceElement, TDestinationElement>(PagedList<TSourceElement> pagedList)
{
return new HybridViewResult<TSourceElement, TDestinationElement>(_mappingService, pagedList);
}
That way you hide the implementation from your controllers, and you don't have to depend on the container.
There are a few different points that you could inject IMappingService. http://codeclimber.net.nz/archive/2009/04/08/13-asp.net-mvc-extensibility-points-you-have-to-know.aspx is a good site for help in picking the appropriate extensibility points for .NET MVC.
If you want to stick with having this functionality be a derived ActionResult, then I think you could put the dependency in the ActionInvoker if you want to, but the Controller makes more sense to me. If you don't want the IMappingService in the Controller, you could always wrap it in a HybridViewResultFactory, and access that object in the Controller. In that case your shortcut methods would look like:
public HybridViewResult<TSourceElement, TDestinationElement> AutoMappedHybridView<TSourceElement,TDestinationElement>(PagedList<TSourceElement> pagedList, string viewNameForAjaxRequest)
{
HybridViewResultFactory.Create<TSourceElement, TDestinationElement>(pagedList, viewNameForAjaxRequest);
}
etc.
I'm not sure why you need to use an ActionResult, but if there is no reason that makes it explicitly necessary, you could create a HybridViewModel class and a HybridViewModelBinder class that is injected with the mapping service dependency.
I am assuming you want to use constructor injection, but if you have the StructureMap dependency in your UI assembly, you could access a static dependency resolver class (like Clowers said).
This question would be easier to give a definite answer to if I understood why you using an ActionResult.
It seems like you are using the action result to handle two functionalities that do not necessarily go together all the time, and that could be used separately. Also, there is not a clear indication that it needs to be in an ActionResult.
Presumably, you could (a) leverage the Automapper functionality for results other than html (ViewResult) output, and (b) you could leverage the functionality of auto-detecting ajax requests without needing to automap the model.
It seems to me like the automapping of the view model could be used to inject the view model into the controller action directly, thus removing the controller's dependency on the IMappingService. What you would need is a ModelBinder class to be injected with your IMappingService (the implementation of which I assume contains a repository or datastore type dependency).
Here is a good article explaining how to leverage model binders: http://odetocode.com/blogs/scott/archive/2009/04/27/6-tips-for-asp-net-mvc-model-binding.aspx.
Then you can overwrite the DefaultModelBinder in the classes that need to be Automapped as follows:
public ActionResult DoItLikeThis([AutoMap(typeof(MyDomainModelClass))]MyViewModelClass viewModel){
//controller action logic
}
Now, regarding the HybridViewResult, I would suggest that you handle this with an Action Filter instead. So, you could just use ActionResult or ViewResultBase as the Result type of your action method and decorate it with an action filter, i.e.:
[AutoSelectViewResult]
public ViewResultBase AndDoThisLikeSo(){
//controller action logic
}
I think overall this will be a much better solution than coupling these two functionalities to an ActionResult.