ASP.NET MVC3 generic controler - c#

I have written a bit of code today which smells somewhat.
public class SomeController : GenericController<SomeViewModel, SomeModel>
Here is a Generic Controller constrained to a particular Model and ViewModel; now what smells is the fact that I am defining the relationship between the Model and the ViewModel I don't mind that the Controller knows about the ViewModel that's fine. What I wish this to do is have the Controller ask the View Model somehow because that's where the coupling should be in my view.
The only way I can think of is in the controller factory. That could inspect the supplied ViewModel and create and instance of the Controller with the Model defined at runtime.
so the above would just become
public class SomeController : GenericController<SomeViewModel, TModel> where TModel : Model
And only be typed at runtime.
any ideas on how to do this? reflection? generics? attributes?
or is this just a really bad idea?
============Edit===========
the reason for the use of generics is there is a lot of shared code throughout the controllers
the controllers use services which intern use repositories.
the services and repositories depend on the type of domain object.
the methods such as public ViewResultBase Add(TViewModel viewModel) in the Generic Controller uses a generic mapper which converts the ViewModel to a Model and passes this to the service -> repository.
============Edit===========
heres a snippet from the base class showing some shared code utilising the generic arguments
[HttpGet]
public virtual PartialViewResult List(int id)
{
var model = BuildListDetails(id);
return PartialView(model);
}
[Dependency]
public IService<TDomainObject> Service { get; set; }
protected IEnumerable<TViewModel> BuildListDetails(int id)
{
var nodes = Service.GetData(UserState.Current.User.UserID, id);
if (nodes == null) return null;
return nodes.Select(n => ModelMapperFactory<TDomainObject, TViewModel>.Instance.Create(n)).AsEnumerable();
}
cheers,

Darin is right (as always). Controllers can work with different models and different views and different view models. Typing your controller to one specific view model and model is just pointless, unless you know for a fact that you will always use just that one view model and just that one model.
There is an association between view models and models. This association is handled in the controller. That's one of its purposes. Don't spend a lot of effort trying to genericize controllers, they typically only contain very specifc code related to its use, and have few options for reuse. When you do need more options, consider using aspects or base clases that just abstract the reusable part (some people authentication logic in a base class, which I don't agree with.. but it's a choice.. other people add their own custom IPrincipal, or other kinds of common features. In most cases, this would not require using generics).

Related

How to inject a repository in a controller when repository is not known at controller construction time?

I have an application, where I do not know the full list of repositories a controller might need upfront (at the time the controller is constructed). A controller gets a list of "components" to render from the database, and then which additional repositories are needed depends on what "components" the database returns. Is there a way to inject these repositories? I'm using ninject although it probably does not matter.
Make your repositories a dependency within your component. Most IOC software like Ninject will inject all the required dependancies for an object when you resolve it.
For example:
public class ComponentA : IComponent
{
public IRepository RepositoryA {get;set;}
}
public class ComponentB : IComponent
{
public IRepository RepositoryAnother {get;set;}
}
Whenever you load ComponentA or B its dependencies (in this case a IRepository) should get loaded as well.
So you don't need to know what Repository is required.
At the time I wrote my question, my "components" were implemented as partial views, and I rendered them from a view executed (Html.Partial) from a controller action. Thanks to this question and answer I changes this model, so that each of my "components" has it's own controller and instead of rendering partial view directly, actions marked with ChildActionOnly on these controllers are called from the view (Html.Action). This is done similar to what is described in the article linked in the original answer, although in my case the view engine was razor.
This way the whole problem does not exist any more - each component controller knows about its repositories now.

Can you validate a model instance using properties from its controller?

Supposing I had, by way of demonstration, a controller that looked like this:
public class ProjectController : Controller
{
private IProjectRepository projectRepository;
public ProjectController()
{
DBContext context = new DBContext();
this.projectRepository = new ProjectRepository(context);
}
public ActionResult Create(Project project)
{
if (ModelState.IsValid)
{
// do whatever
}
else
{
return View(project);
}
}
}
And suppose that this was the controller for a model that looked like this:
public class Project : IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// this is where I would like to add code
}
}
My question is: is there really no way in the validation code to reference the projectRepository object on the controller? Yes, I could technically add quasi-validation to the Create() function on the controller, do the check there, and add errors directly - but in the real case, there are a number of actions that would all perform the same validation, which is really tied to the model you're trying to create (or edit, or copy, or whatever else). But it's tied to the model and to other existing instances of the same model, which only the controller knows how to query for. Is there any way to get around that?
(The goal is to check that the current model object under validation isn't the same as one that exists already; I'm open to other suggestions of how to do that as well, it just seemed like it clearly should be a job for standard validation code, either using IValidatableObject or using a ValidationAttribute. But I am not an expert in .net MVC validation, or for that matter, .net MVC at all.)
Thanks!
IValidatableObject belongs to the DataAnnotations namespace. To my mind, Data Annotations are great for input validation. Where they start to fall down is when you start applying complex business rules where your domain model's validity depends on the state of other domain models.
When that happens, introduce a service layer. Put all your business rules in there, and allow the service to mediate the conversation between your models. At the end of the day, a service is supposed to be the interface with which you communicate with your model.
This is where I usually say to myself, "Hey, your app has now reached the 'medium-complexity' stage"! :)
An older but still relevant tutorial can be found here: http://www.asp.net/mvc/tutorials/older-versions/models-(data)/validating-with-a-service-layer-cs
IMO, there is a bit of a convention issue at play. The model that the controller returns to the client is a ViewModel, not an entity. This comes into play when thinking about which objects have knowledge of dependent objects.
The repository deals with models (entities), and the controller deals with ViewModels. ViewModels are really just a bunch of data and formatting, so set-level validations don't make sense on a ViewModel.
And really want the business-layer or repository to perform set-level validation, not the model itself. You could set a reference to the repository on the model when it is created and have the model call the repository for set-level validation. But this becomes problematic when you want to clone or deserialize the entity.
By the way, EntityFramework solves these problems by allowing you to Attach a disconnected entity. You might want to use EF instead of the repository pattern.
But for you immediate issue, I would not try to perform set-level validation from within the entity or viewmodel.
You might be better of putting your validation in the service layer
The online scaffolding tool CamoteQ generate its model validation code this way, which is a good reference for self-study.

How to use ASP.NET MVC Generic Controller to populate right model

I asked a question about ASP.NET MVC Generic Controller and this answer shows a controller like this:
public abstract class GenericController<T>
where T : class
{
public virtual ActionResult Details(int id)
{
var model = _repository.Set<T>().Find(id);
return View(model);
}
}
Which can be implemented like this.
public class FooController : GenericController<Foo>
{
}
Now when someone requests /Foo/Details/42, the entitiy is pulled from the _repository's Set<Foo>(), without having to write anything for that in the FooController.
The way he explain that is good but think i want to develop a generic controller for product and customer where i will not use EF to load product & customer model rather use MS data access application block.
public abstract class GenericController<T>
where T : class
{
public virtual ActionResult Details(int id)
{
//var model = _repository.Set<T>().Find(id);
var model =customer.load(id);
or
var model =product.load(id);
return View(model);
}
}
So when request comes like /Customer/Details/42 or /product/Details/11 then generic controller's details method will call but how we can detect that request comes from which controller and accordingly instantiate right class to load right model.
If request comes for Customer then I need to load customer details from detail action method or if request comes for Product then I need to load Product details from detail action method of generic controller.
How do I use generics to get the dataset of type T with the Entity Framework Data block?
You may create a set of repositories for working with your entities, like CustomerRepository, ProductRepository from base interface like
public interface IBaseRepository
{
T Get<T>(int id);
void Save<T>(T entity);
}
and then extend your base controller class with repository type and its instance with any of DI frameworks
public abstract class GenericController<T, TRepo>
where T : class
where TRepo : IBaseRepository, new()
{
private IBaseRepository repository;
public GenericController()
{
repository = new TRepo();
}
public virtual ActionResult Details(int id)
{
var model =repository.Get<T>(id);
return View(model);
}
}
Example for CustomerController
public class CustomerController : GenericController<Customer, CustomerRepository>
{
}
where CustomerRepository:
public class CustomerRepository : IBaseRepository
{
public T Get <T>(int id)
{
// load data from DB
return new Customer();
}
}
I don't think it's wise to place data-access and business logic like this in controllers when your application's size and complexity grows beyond a certain point. You should create repositories which handle the data-access and abstract the technology (EF, plain ADO.NET, etc.) away from the consumers. You could use these repositories in your controller, but that would mean that your controllers still contain business logic which you don't want. Controllers should be thin.
What I did was creating a service layer between my repositories and controllers which contain the business logic and delegate data-access to the repositories. I use these services in my controllers to fetch my domain models where I map them to view models. You're gonna want an Inversion of Control container to 'glue' the layers together and to provide loose coupling between them.
A search for 'c# mvc repository and service pattern' will result in loads of examples. I found this post a good one, except for the fact that he returns view models from his services rather than domain models.
This is just my 2 cents, please keep in mind that all of the above only counts when your have a 'mid-range' application and not a typical tutorial/try-out website.
Given my disclaimers in the other question and my comments here explaining why this isn't an ultimate solution, I'll try to give a more concrete implementation:
public abstract class GenericController<T> : Controller
where T : class
{
protected YourEFContext _dataSource;
public GenericController()
{
_dataSource = new YourEFContext();
}
public virtual ActionResult Details(int id)
{
var model = _dataSource.Set<T>().Find(id);
return View(model);
}
}
public class CustomerController : GenericController<Customer>
{
}
This is all code that is needed to let /Customers/Details/42 load customer with ID 42 be loaded from the Entity Framework context. The "generic" part is solved by Entity Framework's DbContext.Set<T>() method, which returns the DbSet<TEntity> for the appropriate entity, in this case DbSet<Customer> which you can query.
That being said, there are many problems with actually using this code:
You don't want to let your controller know about your data access. As you see, a YourEFContext property is used in the controller, tightly coupling it with Entity Framework. You'll want to abstract this away in a repository pattern.
You don't want your controller to instantiate its data access, this should be injected.
You don't want your controller to return database entities. You're looking for ViewModels and a Mapper.
You don't want your controller to do data access. Move the data access in a service layer that also contains your business logic, abstract it again through a repository pattern.
Now your question actually is "Does the Enterprise Library Data Block have a method like GetDataSet<T>", so you don't have to refer to customer and product in your generic controller, but unfortunately I can't find that as I haven't used EntLib for a few years. It would help if you show the code you currently use to access your database.
The ultimate goal you're looking for:
[ MVC ] <=> [ Service ] <=> [ Repository ]
View ViewModel Controller BusinessModel BusinessLogic DataModel Database
Your controller only talks to your service to Create/Read/Update/Delete BusinessModels, and performs the mapping between ViewModels and BusinessModels (at <=>). The service contains the business logic and business model (DataContacts when using WCF) and in turn maps (<=>) to and from DataModels and talks to your repository to persist your models.
I understand this can be a bit much to grasp at once, and that's probably why most ASP.NET MVC tutorials start with all three tiers in one application. Take a look at ProDinner for a more proper approach.

How to expose the DataContext from with-in a DataContext class?

Is it possible to expose the DataContext when extending a class in the DataContext? Consider this:
public partial class SomeClass {
public object SomeExtraProperty {
this.DataContext.ExecuteQuery<T>("{SOME_REALLY_COMPLEX_QUERY_THAT_HAS_TO_BE_IN_RAW_SQL_BECAUSE_LINQ_GENERATES_CRAP_IN_THIS INSTANCE}");
}
}
How can I go about doing this? I have a sloppy version working now, where I pass the DataContext to the view model and from there I pass it to the method I have setup in the partial class. I'd like to avoid the whole DataContext passing around and just have a property that I can reference.
UPDATE FOR #Aaronaught
So, how would I go about writing the code? I know that's a vague question, but from what I've seen online so far, all the tutorials feel like they assume I know where to place the code and how use it, etc.
Say I have a very simple application structured as (in folders):
Controllers
Models
Views
Where do the repository files go? In the Models folder or can I create a "Repositories" folder just for them?
Past that how is the repository aware of the DataContext? Do I have to create a new instance of it in each method of the repository (if so that seems in-efficient... and wouldn't that cause problems with pulling an object out of one instance and using it in a controller that's using a different instance...)?
For example I currently have this setup:
public class BaseController : Controller {
protected DataContext dc = new DataContext();
}
public class XController : BaseController {
// stuff
}
This way I have a "global" DataContext available to all controllers who inherit from BaseController. It is my understanding that that is efficient (I could be wrong...).
In my Models folder I have a "Collections" folder, which really serve as the ViewModels:
public class BaseCollection {
// Common properties for the Master page
}
public class XCollection : BaseCollection {
// X View specific properties
}
So, taking all of this where and how would the repository plug-in? Would it be something like this (using the real objects of my app):
public interface IJobRepository {
public Job GetById(int JobId);
}
public class JobRepository : IJobRepository {
public Job GetById(int JobId) {
using (DataContext dc = new DataContext()) {
return dc.Jobs.Single(j => (j.JobId == JobId));
};
}
}
Also, what's the point of the interface? Is it so other services can hook up to my app? What if I don't plan on having any such capabilities?
Moving on, would it be better to have an abstraction object that collects all the information for the real object? For example an IJob object which would have all of the properties of the Job + the additional properties I may want to add such as the Name? So would the repository change to:
public interface IJobRepository {
public IJob GetById(int JobId);
}
public class JobRepository : IJobRepository {
public IJob GetById(int JobId) {
using (DataContext dc = new DataContext()) {
return dc.Jobs.Single(j => new IJob {
Name = dc.SP(JobId) // of course, the project here is wrong,
// but you get the point...
});
};
}
}
My head is so confused now. I would love to see a tutorial from start to finish, i.e., "File -> New -> Do this -> Do that".
Anyway, #Aaronaught, sorry for slamming such a huge question at you, but you obviously have substantially more knowledge at this than I do, so I want to pick your brain as much as I can.
Honestly, this isn't the kind of scenario that Linq to SQL is designed for. Linq to SQL is essentially a thin veneer over the database; your entity model is supposed to closely mirror your data model, and oftentimes your Linq to SQL "entity model" simply isn't appropriate to use as your domain model (which is the "model" in MVC).
Your controller should be making use of a repository or service of some kind. It should be that object's responsibility to load the specific entities along with any additional data that's necessary for the view model. If you don't have a repository/service, you can embed this logic directly into the controller, but if you do this a lot then you're going to end up with a brittle design that's difficult to maintain - better to start with a good design from the get-go.
Do not try to design your entity classes to reference the DataContext. That's exactly the kind of situation that ORMs such as Linq to SQL attempt to avoid. If your entities are actually aware of the DataContext then they're violating the encapsulation provided by Linq to SQL and leaking the implementation to public callers.
You need to have one class responsible for assembling the view models, and that class should either be aware of the DataContext itself, or various other classes that reference the DataContext. Normally the class in question is, as stated above, a domain repository of some kind that abstracts away all the database access.
P.S. Some people will insist that a repository should exclusively deal with domain objects and not presentation (view) objects, and refer to the latter as services or builders; call it what you like, the principle is essentially the same, a class that wraps your data-access classes and is responsible for loading one specific type of object (view model).
Let's say you're building an auto trading site and need to display information about the domain model (the actual car/listing) as well as some related-but-not-linked information that has to be obtained separately (let's say the price range for that particular model). So you'd have a view model like this:
public class CarViewModel
{
public Car Car { get; set; }
public decimal LowestModelPrice { get; set; }
public decimal HighestModelPrice { get; set; }
}
Your view model builder could be as simple as this:
public class CarViewModelService
{
private readonly CarRepository carRepository;
private readonly PriceService priceService;
public CarViewModelService(CarRepository cr, PriceService ps) { ... }
public CarViewModel GetCarData(int carID)
{
var car = carRepository.GetCar(carID);
decimal lowestPrice = priceService.GetLowestPrice(car.ModelNumber);
decimal highestPrice = priceService.GetHighestPrice(car.ModelNumber);
return new CarViewModel { Car = car, LowestPrice = lowestPrice,
HighestPrice = highestPrice };
}
}
That's it. CarRepository is a repository that wraps your DataContext and loads/saves Cars, and PriceService essentially wraps a bunch of stored procedures set up in the same DataContext.
It may seem like a lot of effort to create all these classes, but once you get into the swing of it, it's really not that time-consuming, and you'll ultimately find it way easier to maintain.
Update: Answers to New Questions
Where do the repository files go? In the Models folder or can I create a "Repositories" folder just for them?
Repositories are part of your model if they are responsible for persisting model classes. If they deal with view models (AKA they are "services" or "view model builders") then they are part of your presentation logic; technically they are somewhere between the Controller and Model, which is why in my MVC apps I normally have both a Model namespace (containing actual domain classes) and a ViewModel namespace (containing presentation classes).
how is the repository aware of the DataContext?
In most instances you're going to want to pass it in through the constructor. This allows you to share the same DataContext instance across multiple repositories, which becomes important when you need to write back a View Model that comprises multiple domain objects.
Also, if you later decide to start using a Dependency Injection (DI) Framework then it can handle all of the dependency resolution automatically (by binding the DataContext as HTTP-request-scoped). Normally your controllers shouldn't be creating DataContext instances, they should actually be injected (again, through the constructor) with the pre-existing individual repositories, but this can get a little complicated without a DI framework in place, so if you don't have one, it's OK (not great) to have your controllers actually go and create these objects.
In my Models folder I have a "Collections" folder, which really serve as the ViewModels
This is wrong. Your View Model is not your Model. View Models belong to the View, which is separate from your Domain Model (which is what the "M" or "Model" refers to). As mentioned above, I would suggest actually creating a ViewModel namespace to avoid bloating the Views namespace.
So, taking all of this where and how would the repository plug-in?
See a few paragraphs above - the repository should be injected with the DataContext and the controller should be injected with the repository. If you're not using a DI framework, you can get away with having your controller create the DataContext and repositories, but try not to cement the latter design too much, you'll want to clean it up later.
Also, what's the point of the interface?
Primarily it's so that you can change your persistence model if need be. Perhaps you decide that Linq to SQL is too data-oriented and you want to switch to something more flexible like Entity Framework or NHibernate. Perhaps you need to implement support for Oracle, mysql, or some other non-Microsoft database. Or, perhaps you fully intend to continue using Linq to SQL, but want to be able to write unit tests for your controllers; the only way to do that is to inject mock/fake repositories into the controllers, and for that to work, they need to be abstract types.
Moving on, would it be better to have an abstraction object that collects all the information for the real object? For example an IJob object which would have all of the properties of the Job + the additional properties I may want to add such as the Name?
This is more or less what I recommended in the first place, although you've done it with a projection which is going to be harder to debug. Better to just call the SP on a separate line of code and combine the results afterward.
Also, you can't use an interface type for your Domain or View Model. Not only is it the wrong metaphor (models represent the immutable laws of your application, they are not supposed to change unless the real-world requirements change), but it's actually not possible; interfaces can't be databound because there's nothing to instantiate when posting.
So yeah, you've sort of got the right idea here, except (a) instead of an IJob it should be your JobViewModel, (b) instead of an IJobRepository it should be a JobViewModelService, and (c) instead of directly instantiating the DataContext it should accept one through the constructor.
Keep in mind that the purpose of all of this is to keep a clean, maintainable design. If you have a 24-hour deadline to meet then you can still get it to work by just shoving all of this logic directly into the controller. Just don't leave it that way for long, otherwise your controllers will (d)evolve into God-Object abominations.
Replace {SOME_REALLY_COMPLEX_QUERY_THAT_HAS_TO_BE_IN_RAW_SQL_BECAUSE_LINQ_GENERATES_CRAP_IN_THIS INSTANCE} with a stored procedure then have Linq to SQL import that function.
You can then call the function directly from the data context, get the results and pass it to the view model.
I would avoid making a property that calls the data context. You should just get the value from a service or repository layer whenever you need it instead of embedding it into one of the objects created by Linq to SQL.

How do I share code among controllers of different models?

How do I share code among controllers of different models?
In a 'super' controller action, I want to be able to figure out which controller (route) was called, and using that route name load the corresponding model, and use that model to query the database to pass to the view.
I can easily do this in Ruby on Rails, does MVC allow such a thing?
You could use Generics:
public abstract class GenericRepositoryController<TModel> : Controller
{
private readonly IRepository<TModel> _repository;
protected GenericRepositoryController(IRepository<TModel> repository)
{
_repository = repository;
}
public ActionResult Index()
{
var model = _repository.GetAll();
return View(model);
}
}
public class EmployeeController : GenericRepositoryController<Employee>
{
public EmployeeController(IRepository<Employee> repository) : base(repository)
{
}
}
public class CustomerController : GenericRepositoryController<Customer>
{
public CustomerController(IRepository<Customer> repository) : base(repository)
{
}
}
Sure, you could have all routes point to a central controller if you so desired and then redirect. Alternatively, you could have a base controller that other controllers inherrited from that did this as well.
Couldn't you use a base class for all your controllers and add the logich do figure out the route in the base class?
Or extract the 'rule finding method' to a helper class and use that in each method.
Think of your models as data structures, simple classes. They should have properties of course, but typically not code. The code should be organized elsewhere so that it can be shared.
How depends on the nature of the code you want to share. If it's controller level logic, related to model or view manipulation for example, it might be best in a controller base class. If it's business logic it should be in a service. Think of services as a layer of business logic between your controllers and your repository or database (depending on if you are using IOC). Your services should be contained in a library project referenced by your site, so that any controller can reference them.
Your routing rules should land you in appropriate controllers based on naming conventions, invoking action methods. Those methods may call any code from other classes or referenced projects.

Categories

Resources