Correct way to connect controllers using Dependency Injection - c#

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

Related

Generic C# Repository, service and controller design

Im learning about generics and was wondering about how a generic controller, service and ef core repo design would look like.
My case: lets say an incomming post request to add Smartphone and keyboard object to smartphone and keyboard tables
My repository setup is
public class GenericRepository<TEntity> : IGenericRepository<TEntity>
where TEntity : class, IProductGenericEntities
{
private readonly MyDbContext _db;
public GenericRepository(MyDbContext db)
{
_db = db;
}
public async Task<bool> AddProduct(TEntity entity)
{
try
{
_db.Set<TEntity>().AddAsync(entity);
return (await _db.SaveChangesAsync()) > 0;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return false;
}
}
}
And my service
public class ProductService<TEntity> : IProductService<TEntity>
where TEntity : class
{
private readonly IGenericRepository<TEntity> _repo;
public ProductService(IGenericRepository<TEntity> repo)
{
_repo = repo;
}
public async Task<bool> AddProduct(TEntity entity)
{
return await _repo.AddProduct(entity);
}
}
And my Controller.cs
[ApiController]
[Route("api/[controller]")]
public class ProductController
{
private readonly IProductService<Keyboards> _keyService;
private readonly IProductService<Smartphones> _smartService;
public ProductController(IProductService<Keyboards> keyService, IProductService<Smartphones> smartService)
{
_keyService = keyService;
_smartService = smartService;
}
[HttpPost("Post-generated-items")]
public async Task<ActionResult> PostProducts(List<TEntity> entities)
{
foreach(var item in entities)
{
and sort the objects here
}
}
}
is it correct to initialize 2 of IProductServices and sort the incomming objects to their correct DI on the controller?
private readonly IProductService<Keyboards> _keyService;
private readonly IProductService<Smartphones> _smartService;
Is there a way to make it more automatic by detecting incomming object type and then initilize it all the way to repo so i dont need 2 of IProductService<>?
Or is it what im doing plain wrong with a generic service layor?
Ok, so your approach is completely valid, i would not worry about initializing two repositories, since they're essentially empty memory vise since they just take reference to existant DbContext which by default is registered with Scoped lifecycle.
There will be a time when you need to use several repositories to complete task at hand. I would suggest going for NON-generic services approach. This way you could make ProductsService which has all the needed generic repositories injected and can orchestrate their work to achieve use case goal.
You might as well look into UOW (Unit Of Work) pattern for even more complex situations.
Answering your question:
Is there a way to make it more automatic by detecting incomming object type and then initilize it all the way to repo so i dont need 2 of IProductService<>?
You might write some code that would do just that for you using Reflection, but i would suggest against doing so. By initializing your repositories specifically you make yourself less error prone and code becomes more self-documenting.
For example now you have a controller that asks DI for two services and that instantly set's you up for what's going on in this controller. On the other hand if everything would be generic, you would end up with one huge knot of spaghetti that "Does everything".

One controller for different area views

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;
}
}

Dependency Injection when controller called from another controller

I have a single ASP.NET 5.0 (vnext) project where I am implementing both a Web Api and an Mvc front end. I want my Mvc controller to call the Web Api controller, which is working just fine. I built the api based on the example at http://www.asp.net/vnext/overview/aspnet-vnext/create-a-web-api-with-mvc-6, and it is working great. The Mvc front end can call the WebApi controller successfully, but the ITodoRepository doesn't get provided by the dependency injection framework when I instantiate it from the Mvc controller.
public class Startup
{
public void Configure(IApplicationBuilder app, ILoggerFactory logFactory)
{
...
app.UseServices(services =>
{
services.AddSingleton<ITodoRepository, TodoRepository>();
});
...
[Route("api/[controller]")]
public class TodoController : Controller
{
/* The ITodoRepository gets created and injected, but only when the class is activated by Mvc */
TodoController(ITodoRepository repository)
{
_repository = repository;
}
[HttpGet]
public IEnumerable<TodoItem> Get()
{
return _repository.AllItems;
}
...
public class HomeController : Controller
{
public IActionResult Index()
{
var tc = new TodoController(/* have to create my own ITodoRepository here */);
return View(tc.Get());
}
...
I was able to add an ITodoRepository to the HomeController with the [Activate] attribute, and then pass that to the constructor for the TodoController, but that doesn't pass the smell test to me. Home Controller shouldn't have to have or even know about those.
Is there another way to create the TodoController instance that will invoke the DI logic and provide the dependencies?
If you're concerned about code smell, the main concern should be about having one controller calling another controller.
Controllers are meant to be called in two scenarios:
By the system (i.e. MVC)
By your unit tests
Instead, I recommend having both controllers call a business logic component that itself might use dependency injection to acquire its dependencies, and that each controller perhaps use dependency injection to acquire the business logic dependency as well.
public class HomeController : Controller {
public HomeController(IMyAppBusinessLogic bll) { ... }
}
public class WebApiController : Controller {
public WebApiController(IMyAppBusinessLogic bll) { ... }
}
public class MyAppBusinessLogic : IMyAppBusinessLogic {
public MyAppBusinessLogic(ITodoRepository repository) { ... }
}
Any middleware registered using app.UseServices are available only within the scope of a web request. There is no web request context when you are trying to instantiate the webapi controller directly from your MVC app and therefore the dependencies will not be resolved.
It's normal to create an execution context manually for the purposes of unit testing. Not sure which DI framework are you using but I do something like the following in my project (OWIN not vNext) which is using SimpleInjector
public static void UseInjector(this IAppBuilder app, Container container)
{
// Create an OWIN middleware to create an execution context scope
app.Use(async (context, next) =>
{
using (var scope = container.BeginExecutionContextScope())
{
await next.Invoke();
}
});
}

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.

How to scope out Dbcontexts (to prevent singleton context for entire application)

I was wondering how do you scope out your Dbcontexts in Entity Framework so you don't use a single Dbcontext for your entire application. I am new to Entity Framework and have been reading tutorials, but they all used a single Dbcontext as an example, so EF is pretty much a blackbox for me right now.
Let's say for example I have 3 models:
Post
User
Comment
Each model is related to each other (A Post belongs to User, Comment belongs to User and Post). Do I make a Dbcontext for each one individually? But that wouldn't be correct since they are all related, or would I make a Dbcontext for each scenario that I need? For example, if I only need to query for Post and Comments and not user, that would be a PostCommentsContext. And then we would have a PostUserCommentContext...
The best solution would be to use a Unit of Work to wrap the Data Context, as well as managing the connection lifetime and allowing you to work with multiple Repositories (if you were so inclined to go down that path).
Summary of implementation:
Create an interface (IUnitOfWork) which exposes properties for your DbSet's, as well as a single method called Commit
Create an implementation (EntityFrameworkUnitOfWork), implementing as required. Commit simply calls SaveChanges on the base class (DbContext), and also provides a good hook-in for last minute logic.
Your controller accepts a IUnitOfWork, use DI (preferably) to resolve a EntityFrameworkUnitOfWork, with a HTTP-context scoped lifetime setting (StructureMap is good for this)
(optional, but recommended) create a Repository which also takes the IUnitOfWork, and work off that via your Controller.
HTH
EDIT - In Response to Comments
Oh, how can you do work that involves creating records in multiple models then? i.e., create a new user and a new post in the same transaction.
Given your using ASP.NET MVC, your controllers should accept an IUnitOfWork in their constructor.
Here's an example, based on what you asked
public SomeController : Controller
{
private IUnitOfWork _unitOfWork;
private IUserRepo _userRepo;
private IPostRepo _postRepo;
public SomeController(IUnitOfWork unitOfWork, IUserRepo userRepo, IPostRepo postRepo)
{
_unitOfWork = unitOfWork; // use DI to resolve EntityFrameworkUnitOfWork
_userRepo = userRepo;
_postRepo = postRepo;
}
[HttpPost]
public ActionResult CreateUserAndPost(User user, Post post)
{
// at this stage, a HTTP request has come in, been resolved to be this Controller
// your DI container would then see this Controller needs a IUnitOfWork, as well
// as two Repositories. DI smarts will resolve each dependency.
// The end result is a single DataContext (wrapped by UoW) shared by all Repos.
try
{
userRepo.Add(user);
postRepo.Add(post);
// nothing has been sent to DB yet, only two objects in EF graph set to EntityState.Added
_unitOfWork.Commit(); // two INSERT's pushed to DB
}
catch (Exception exc)
{
ModelState.AddError("UhOh", exc.ToString());
}
}
}
And one more question, what does the HTTP-context scoped lifetime do?
Objects in DI-talk have scope management settings that include per thread, per session, per http request, singleton, etc.
HTTP-context scoped is the recommended setting for web apps. It means "new up a context when a HTTP request comes in, and get rid of it when the request is finished".
Use 1 DbContext! That will make life easier for you. Don't worry about performance, data that isn't needed or queried won't be loaded and won't consume any resources.
public class UserContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<Comment> Comments { get; set; }
}
For some scenarios you might want 2 or more contexts.
A context like the one above to hold all the front-end data needed for your application to work and another context for - as an example - to store reports generated from that front-end data, and which is only used in the back-end of you application.
I am experimenting with UnitofWork, here is what I have come up with...
First I created a IUnitofWork that only contains one method. Commit();
Then my dbContext looks like this
public class myContext : DbContext, IUnitOfWork
{
public DbSet<Users> Users { get; set; }
public DbSet<Addresses> Address { get; set; }
public void Save()
{
SaveChanges();
}
}
My repository classes take a UnitofWork in their ctors.
public class UserRepository : IRepository<Position>
{
private myContext _context;
public UserRepository (IUnitOfWork unitOfWork)
{
if (unitOfWork == null)
throw new ArgumentNullException("unitOfWork");
_context = unitOfWork as myContext;
}
/// other methods ///
}
Then the code in the controller would be something like this
_unitOfWork = new myContext();
_userDB = new UserRepository(_unitOfWork);
_addressDB = new AddressRepository(_unitOfWork);
_userDB.Add(newUser);
_addresesDB.Add(newAddress);
_unitOfWork.Save();
I have debugged and proved that no data is commited until the Save method of the _unitOfWork is called. Very cool stuff!!

Categories

Resources