ASP.NET Core MVC View Models - best practise - c#

As far as I'm aware, this isn't a major issue, but I was more curious to see if anyone could offer their expertise regarding some code I'm reviewing. Basically, I've got a view model that is taking a controller class as an argument in its constructor to then store a bunch of properties from the controller class.
I don't want to assert that this is bad practise without being able to explain why, but this isn't a design pattern that I've typically seen when working on MVC web apps, and I'm not convinced it's a great idea to introduce coupling between your view models and your controllers in this way. Is this approach to instantiating your view model okay, or should it be avoided?
Some pseudo code:
public class SomeController : Controller {
public SomeController() {
}
public Index() {
var vm = new ViewModel(this);
return View("Index", vm);
}
}
public class ViewModel()
{
public ViewModel(SomeController controller) {
}
}
The closest answer I've found to this is here, but in this case we're not dealing with dependency injection.

Related

Dependency Injection of DbContext without Repository

I am trying to learn how to use Dependency Injection on an auto-scaffolded controller in an MVC 5 Web application. While I have found tutorials on "Dependency Injection" and "Unity", the examples I have seen are more of the hello world variety and never actually get around to dealing with database contexts in controllers.
To give you a better example of what I mean, here is a code snippet of what I have and what I am trying to do.
When I use Visual Studio to auto-scaffold a controller "with views, using Entity Framework", this is typically what is created.
public class TideUploadsController : Controller
{
// This is the EF Database context placed by the auto-scaffolder
private AzMeritContext db = new AzMeritContext();
[HttpGet]
public ActionResult Index()
{
return View(db.TideUploads.ToList());
}
/* further CRUD ActionResults not shown */
}
}
From what I read so far, to use Dependency Injection, I should use a constructor that calls an interface instead of a class. I have read mixed reviews about wrapping a repository around Entity Framework. I would like to see if I could do this without use of a repository.
Is there a pre-existing interface I should use in place of the DbContext that is placed in the code by the auto-scaffolder?
// This is the EF Database context placed by the auto-scaffolder
private AzMeritContext db = new AzMeritContext();
public TideUploadsController(/* what Interface goes here? */)
{
//db = ?
}
Or is this a situation where I really need to wrap a repository around the Entity Framework dbContext?
I have spent the last few weeks reading books and searching through tutorials and videos, and I have not yet seen a step-by-step example that shows something like:
Step 1: Take the controller that you just auto-scaffolded
Step 2: Do this...
Step n: And now your controller is loosely-coupled with your database context rather than tightly-coupled.
Well, for me one of the benefits behind dependency injection is that you basically are decoupling your "dependencies" into mockable objects or other implementations, IMHO repository pattern is the common way to go, I don't see any other alternative of having to use a wrapper and configure you interface and your implementation in your container.
I guess you are familiar with the concept:
public class UserRepository:IUserRepository
{
public UserRepository(){
// you usually instanciate your context here
// private AzMeritContext db = new AzMeritContext();
}
User GetUserById(int Id){
// do your query to get a single user
}
}
this implementation looks ok to me.
then your controller should look like this:
public class TideUploadsController : Controller
{
public TideUploadsController(IUserRepository userRepository){
// constructor injection
// assign your user repository to a local variable outside of the constructor scope something like _userRepository
}
[HttpGet]
public ActionResult Index()
{
return View(_userRepository.GetUserById(1));
// let's assume your variable's name is _userRepository
}
/* further CRUD ActionResults not shown */
}
}
just remember that your interface should implement the methods you would like to use and expose in your controllers, services, etc.
my two cents, I hope that helps.

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.

ASP.NET MVC3 generic controler

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).

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.

ASP.NET MVC: Populating Derived Strongly Typed View with Base Strongly Typed View

In my application I have different pages: Contact Us, About Us, Home
They all have the same base elements that needs to populate the view:
Page Title
Meta Description
User Information
However on each page, they have some elements that are different:
Contact Us
Contact Information Model
Contact Form Model
About Us
Extended User Information Model
Home
Home Page Text Property
They are all routed to the same Controller Action Method because most of the functionality is similar other than populating the "extra" information dependent on page type.
So far I have done something where:
PageDetailViewData pageDetailViewData = new PageDetailViewData {Title = title, Desc = desc....}
and following this I have:
switch ((PageType)page.PageType)
{
case (PageType.Contact):
return View("ContactUsDetails", pageDetailViewData);
default:
return View(pageDetailViewData);
}
The question is how do I populate the "extra" information? I am not sure if I am going about doing this the right way. Any insight to better structure the logic flow would be appreciated.
The answer of using interfaces to imply some commonality between your view models is certainly going to help to answer some of the points in your questions.
I would however ask how wise it is to "refactor" your Action to support multiple views of differing data structures.
MVC controller actions typically represent the minimum amount of code required to gather the specific data required to generate the intended view. It's not completely uncommon for a single action to return different views of the same model data (Html view or Mobile view for example) but by varying both the structure of the data and view that will generated you introduce a few problems.
In particular you violate common best practices like the Single Responsibility Principle and make your code much more complicated to test - and Pain free testing and TDD are part of the big win with ASP.Net MVC after all.
Personally I would have a separate Action.
As far as your view models are concerned, how would you do it if this was a database?
You would have separate queries for separate data right?
A user's profile information would be queried separately from the page meta data information. This would be done for a number of reasons that could include the ability to cache certain parts of the data but not others for example.
So with the above suggestions your code might look like this (Warning: this code wasn't written in Visual Studio and is probably full of syntax issues):
public interface IMetaDataViewModel
{
PageMetaData MetaData{get; set;}
}
public class HomeViewModel : IMetaDataViewModel
{
public PageMetaData MetaData{get; set;}
public string HomePageText{get; set;}
}
//other view models go here....
public class CommonPagesController : Controller
{
private MetaDataProvider _metaProvider = new MetaDataProvider();
private PageDataProvider _pageDataProvider = new PageDataProvider();
private ContactDataProvider _contactDataProvider = new ContactDataProvider();
public ActionResult Home()
{
var viewModel = new HomeViewModel
{
MetaData = _metaProvider.GetPageMeta();
HomePageText = _pageDataProvider.GetPageData();
};
return View(viewModel);
}
public ActionResult Contact()
{
var viewModel = new ContactViewModel
{
MetaData = _metaProvider.GetPageMeta();
ContactFormData = _contactDataProvider.GetData();
};
return View(viewModel);
}
//you get the picture...
}
There are several ways you could also refactor out the generation of the view model code but thats one possible pattern.
I appreciate that this answer does have a certain amount of opinion in it but I would consider having separate actions to be best practice.
Hope that helps.
The title of your question almost gives you the answer. You can use some form of polymorphism to accomplish this. You could define a base class with the shared properties, or alternatively an interface like this:
public interface ICommonPage
{
string Title { get; }
string MetaDescription { get; }
string UserInformation { get; }
}
Then define three strongly typed ViewModel classes that all implement this interface (or derive from the base class):
ContactUsViewModel : ICommonPage
AboutUsViewModel : ICommonPage
HomeViewModel : ICommonPage
On each of those ViewModel classes, you add the extra properties that you need for those Views.
In your Controller Action, you will need to switch on PageType to select the correct ViewModel and populate it with data.
You will also need to creat three different Views (.aspx) that are strongly typed to each ViewModel class.
If you have shared rendering for the common data, you can extract that into a strongly typed UserControl (.ascx) that is typed to ICommonPage.

Categories

Resources