One controller for different area views - c#

I have MVC5 app which has 2 areas: area1 and area2.
Some views in those areas are using code from controller which is actually same for view in area1 and view in area2.
Now I have 2 controllers per each area, but as I mentioned, the code is very same.
How can I use one Controller per each view in the area 1 and 2 to avoid code duplicity and have simpler maintainability?

Areas are just a layer, but they can still interact with each other and the application at large. If you need to share a controller, you can simply subclass it. Better yet, create an abstract controller outside of the areas and inherit each area's controller from that.
As far as views go, Razor has a very easy convention for handling overrides and fallbacks. It searches multiple directories, based on convention, for the required view and stops only when it finds a match.
For example, if you placed the view in Views\Shared, the last resort fallback, it can be used literally anywhere in your application, including each of your areas. The order of ops for view location is:
Areas\[AreaName]\Views\[ControllerName]
Areas\[AreaName]\Views\Shared
Views\[ControllerName]
Views\Shared
Razor will go down the list looking for the view in each location until it finds it.

You keep the separate controllers as they serve their purposes in the MVC framework. However, you can export much of the code in the controllers to service classes and each controller uses the services as needed -- Now you avoid code duplicity and have simpler maintainability.
Controllers
public class HomeController : Controller
{
private IFooService service;
public HomeController()
{
this.service = new FooService(dbContext);
}
public ActionResult CalculateFoo(int id)
{
var foo = this.service.CalculateFoo(id);
return View(foo);
}
}
public class FooController : Controller
{
private IFooService service;
public FooController()
{
this.service = new FooService(dbContext);
}
public ActionResult Details(int id)
{
var foo = this.service.CalculateFoo(id);
return View(foo);
}
}
Service
public class FooService : IFooService
{
private DbContext db;
public FooService(DbContext db)
{
this.db = db;
}
public Foo CalculateFoo(int id)
{
var foo = this.db.Foo.First(f => f.id == id);
// do stuff
return foo;
}
}

Related

Correct way to connect controllers using Dependency Injection

If i have a controller that receives and processes the action selected by the user, and then want to use another controller to store all database related logic, what is the correct way to connect these controllers while allowing the 2nd controller to interact with the database context.
At the moment I have it working with creating a database context in the first controller and then parsing that to the database controller when I connect the two using DI, but hopefully someone could show me the correct way to do this.
public class TestController : Controller
{
private readonly DatabaseContext context;
private Database.UserController userDatabaseController;
public TestController(DatabaseContext db)
{
context = db;
userDatabaseController = new Database.UserController(context);
}
}
database controller
public class UserController : Controller
{
private readonly DatabaseContext context;
public UserController(DatabaseContext ctx)
{
context = ctx;
}
public bool RegisterUser(Models.DatabaseModels.UserModel model)
{
try
{
context.Users.Add(model);
context.SaveChanges();
return true;
}
catch (Exception e)
{
return false;
}
}
}
startup.cs
services.AddDbContext<DatabaseContext>
(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
databasecontext
public class DatabaseContext : DbContext
{
public DatabaseContext(DbContextOptions<DatabaseContext> options)
: base(options)
{ }
public DbSet<DatabaseModels.UserModel> Users { get; set; }
}
The "correct" way is: you don't. A controller should never directly call into another controller. You can redirect to a new route that maps to a different controller, but that's it. If you have some common logic that needs to be shared, then that should be factored out into a completely different class that both controllers can utilize.
If you're finding that you need to call Controller methods from another Controller, you probably need to refactor your code. Controllers should have very little logic in them, which usually just involves calling a Service layer and then constructing a ViewModel from the data.
My advice would be to do some reading on the Service Layer pattern and the Repository pattern (sometimes called the Manager pattern).

Should same methods from different controllers be moved to a CommonController?

I have two controllers that have few same methods:
public class Controller1 : Controller
{
private readonly ITestBL bl;
public Controller1(ITestBL bl)
{
this.bl= bl;
}
[HttpGet]
public ActionResult Method1(string data)
{
using (bl)
{
var res = ...
return Json(res, JsonRequestBehavior.AllowGet);
}
}
[HttpGet]
public ActionResult Method2(string data, int data2)
{
using (bl)
{
var res = ...
return Json(res, JsonRequestBehavior.AllowGet);
}
}
// other methods
}
And the second controller also has those two methods.
Should I create some common controller to keep those methods? So, it will look like this:
public abstract class CommonController: Controller
{
private readonly ITestBL bl;
protected Controller1(ITestBL bl)
{
this.bl= bl;
}
[HttpGet]
public ActionResult Method1(string data)
{
using (bl)
{
var res = ...
return Json(res, JsonRequestBehavior.AllowGet);
}
}
[HttpGet]
public ActionResult Method2(string data, int data2)
{
using (bl)
{
var res = ...
return Json(res, JsonRequestBehavior.AllowGet);
}
}
}
And my Controller1 and Controller2 will be:
public class Controller1 : CommonController
{
private readonly ITestBL bl;
public Controller1(ITestBL bl)
:base(bl)
{
}
// methods
}
Is that the proper way to do that? Do I miss anything or is there a better way?
Should same methods from different controllers be moved to a CommonController?
Yes and you should not use Inheritance. I'm sure there are plenty of people who may disagree, however your example is extremely generic and gives very poor context there is no good reason all controllers need the same code (Inheritance or not). Your questions context has no reason for it to be the case in the OOP realm of Has A vs Is A (Excerpt below).
A House is a Building (inheritance);
A House has a Room (composition);
What it appears you are doing is neither of these.
If your interface was IVehicleEngine and your controllers were FerarriVehicleController and FordVehicleController now it makes sense in a context. In this case each controller should use inheritance, it makes sense.
In my own humble opinions, inheriting from your own controller can be quite difficult in multiple terms. First, it's not intuitive; that is it will become tribal knowledge because it replaces a normal convention that most programmers adhere to (that is deriving from the base MVC controller). Secondly, I've seen it become the one place that everyone decides to add code to (God Object) even though it may not be applicable from some controllers. Thirdly, it makes it difficult to reuse url's that make sense for the derived type but not the base type (/search?searchFor=). There are a number of other considerations that are very specific to MVC because of it's exposure to the web (security etc etc).
Depending on implementation you may also experience difficulty in determining which URL to use under what circumstances.
Is /Controller1/Method1/Data/1 the same as /Controller2/Method1/Data/1 but different from /Controller3/Method1/Data/1? If they are all the same or some are the same and some are different then there is most likely something wrong with the architecture.
Nothing wrong with inheriting from a base controller. As it adheres to the DRY principle. I would go with :
public abstract class CommonController: Controller
{
protected readonly ITestBL bl;
protected Controller1(ITestBL bl)
{
this.bl= bl;
}
[HttpGet]
public virtual ActionResult Method1(string data)
{
var res = ...
return Json(res, JsonRequestBehavior.AllowGet);
}
[HttpGet]
public virtual ActionResult Method2(string data, int data2)
{
var res = ...
return Json(res, JsonRequestBehavior.AllowGet);
}
}
Main differences being.
I removed "using". Like the others have said it's best to let your DI framework decide when to dispose of the injected class.
Make the action results virtual. The inheriting controllers may have the same requirements now, but there is no guarantee that they will stay that way in the future. So declaring them as virtual allows for future changes in scope/requirements as they can be overridden.
I made the injected class "protected" rather then "private" as other methods in both inheriting controllers may need it too.

Abstracting a large number of similar ASP.NET MVC Actions (C#)

A new MVC 5 app that I'm working on references a large-ish collection (4 assemblies, about 500 classes each) of data models generated from a 4GL environment. The basic interaction has the MVC app present and populate a model instance, then (after model validation), hand the model instance off to a provider for processing.
The initial approach I've used is, for each model, create
a scaffolded razor view bound to the model,
a partial controller class with a pair of actions (GET/POST) the model
All of the actions are part of the same controller class which has a couple of private methods to implement the GET & POST actions exposed in each of the partials.
So, the structure is like:
|
|--\Controllers
|
|--MyController.cs
|--MyController.MDL001.cs
|--MyController.MDL002.cs
|-- ...
|--MyController.MDL500.cs
|--\Views
|
|--\My
|--\MDL001.cshtml
|--\MDL002.cshtml
|-- ...
|--\MDL500.cshtml
And the implementation of each partial controller follows the pattern:
public partial class MyController
{
public ActionResult ProcessMDL001(MDL001Model modelInstance)
{
return ProcessModel(modelInstance);
}
public ActionResult MDL001()
{
return ShowModel("MDL001");
}
}
Where methods ProcessModel(...) and ShowModel(...) are defined in MyController.cs
I want to keep MVC's model binding and validation functioning but also am keen on avoiding a few thousand nearly-identical concrete action implementations. Is there some pattern/approach using routing, generics, dynamics, etc. that can help here?
Assuming that you can roughly treat each class the same, you can handle this with generics:
public class BaseController<T> : Controller
where T : class, new
{
public ActionResult Process(T modelInstance)
{
return ProcessModel(modelInstance);
}
...
}
But, you would need to use subclasses instead of partial classes. Essentially, you're just going to implement the action once, and then subclass this base controller to specify the type for the controller instance you're working with:
public MDL001ModelController : BaseController<MDL001Model>
{
}
If no additional type-specific actions are needed, then that code alone is all you subclass would need to be. However, you can always add additional actions that will only apply to this particular controller instance.
If there are pieces of the common actions that you need to customize slightly, such as validation logic or something, you can provide hooks in your actions. Something along the lines of:
public class BaseController<T> : Controller
where T : class, new
{
public ActionResult ActionWithHook(T model)
{
DoSomeWork(model);
return View();
}
// By default this does nothing, but can be overridden to do something
internal virtual void DoSomeWork(T model)
{
}
}
Then:
public MDL001ModelController : BaseController<MDL001Model>
{
internal override void DoSomeWork(MDL001Model model)
{
// Do something
}
}

Using a DbContext variable from one Controller to Another

Hi I am using MVC 4 and C# to develop an application that has two controllers:
The first one is called Business, it has a method called Create that calls a method called CreatePartner from another Controller named PartnerController.
public class BusinessController : Controller
{
private storeContext db = new storeContext();
public ActionResult Create(Business business)
{
//Some stuff here
PartnerController pt = new PartnerController();
pt.CreatePartner(int partner_id);
//Here is another stuff that uses db DbContext variable
return RedirectToAction("Index");
}
}
This is the second controller Called Partner
public class PartnerController : Controller
{
private storeContext db = new storeContext();
public void CreatePartner(int partner_id)
{
//Some interesting stuff
}
}
Each controllers has its Dispose() method
The Problem is: After I called the CreatePartnet method from Business controller I try to use the db variable again to save other data but it throws me the following exception:
The operation can not be completed because the DbContext has been disposed
-What is the best way to Use methods from one controller to another that has the same DbContext variable name?.
-Something strange happens: My stuff works locally but when I publish my code in the IIS server is when the app throws that exception.
Thanks!
Might I suggest an alternative approach?
Controllers are not very good places for business logic; that is they're not very good places for "doing stuff". It's often demonstrated in MVC tutorials and examples in this manner but it's really only good for getting into MVC quickly - it's not very good practice.
Furthermore Controllers aren't really supposed to have methods to be called - from themselves or called from another Controller. Controllers should really just contain their Actions.
Instead, extract your logic to an external class. A Service is a design pattern in which commonly used business logic is abstracted away. That way things can have a reference to the service and execute the logic without knowing anything about the implementation.
Observe:
IPartnerService
public interface IPartnerService
{
void CreatePartner(int partnerId);
}
DefaultPartnerService
public class DefaultPartnerService : IPartnerService
{
private StoreContext db;
public DefaultPartnerService()
{
db = new StoreContext();
}
public void CreatePartner(int partnerId)
{
// Something interesting
}
}
BusinessController
public class BusinessController : Controller
{
private IPartnerService _partnerService;
public BusinessController()
{
_partnerService = new DefaultPartnerService();
}
public ActionResult Create(Business business)
{
_partnerService.CreatePartner(business.PartnerId);
return RedirectToAction("Index");
}
}
Of course this approach is also greatly simplified for educational purposes. It's not best practice yet, but it might put you on the right track. Eventually you'll discover problems with this approach and you'll gravitate to reading about Repositories, Unit of Work, Dependency Injection and so on.

Get instance of objects based on other values without using service location

I am using a Unit of Work pattern in my mvc 3 app with Ninject. I have run into a problem where i'm having difficulty solving it without using new or service location of some kind.
I am using an abstract base model called MyModel which has 2 concrete subclasses MyModel1 and MyModel2. These need to get sent to the view based on a value set in the users record.
public class MyController : Controller
{
private IUnitOfWork _unitOfWork;
public MyController(IUnitOfWork unitOfWork) { _unitOfWork = unitOfWork; }
...
public ActionResult MyMethod() {
var user = _unitOfWork.Get(user)
if (user.myprop == somevalue)
{
return View(new MyModel1());
}
return View(new MyModel2());
}
This works fine (although I don't like it, it's simple and works. The problem is that using new is an anti-pattern when using Dependancy Injection. Additionally, I now want the models to initialize themselves (from the database) so I need to inject the IUnitOfWork into the constructor of the models. I could, of course do this:
if (user.myprop == somevalue)
{
return View(DependancyResolver.Current.GetService(typeof(MyModel1)));
}
But Service Location is also an anti-pattern.
Any suggestions on how to solve this?
Using new is not an anti pattern for DI if used correctly. There is absolutely no problem to use new to create data containers such as view models.
But it is an anti pattern for MVC applications to have logic or data retrieving code in your view models so that they need dependencies. All this stuff belongs outside into the controller or some services. The data is assigned preformatted to the view model from outside.
Additionally, I now want the models to initialize themselves (from the
database) so I need to inject the IUnitOfWork into the constructor of
the models
No. You should not pass any models to your views. You pass VIEW MODELS only. View models are dumb. They only contain preformatted data for the view to display. If you used AutoMapper for example you could have externalized this into the mapping layer and your controller could become:
public ActionResult MyMethod() {
var user = _unitOfWork.Get(user)
var userViewModel = Mapper.Map<User, UserViewModel>(user);
return View(userViewModel);
}

Categories

Resources