public class BaseController : Controller
{
[Inject]
public IUnitOfWork UnitOfWork { get; set; }
private readonly ISomeService _someService ;
public BaseController(ISomeService someService)
{
_someService = someService;
}
public void Contacts()
{
contacts = _someService .GetById(1);
ViewBag.someThing = contacts; //Add whatever
}
public BaseController()
{
}
}
While I'm sending someService in the :base I can get the data from it. However I don't want to send someService from each controller like AboutController to the BaseController and to write too much code.
public class HomeController : BaseController
{
private readonly ISomeService someService;
public HomeController(ISomeService someService) : base(someService)
{
_someService = someService;
}
}
public class AboutController : BaseController
{
private readonly IAboutService _aboutService;
public AboutController (IAboutService aboutService)
{
_aboutService = aboutService;
}
}
So in AboutController view still i wanna get someService's data without sending parameter to the BaseController
The short answer would be separated in two cases:
If you want to use ISomeService from the base controller you are obliged to pass that service through base constructor like this:
public class AboutController : BaseController
{
private readonly IAboutService _aboutService;
public AboutController (IAboutService aboutService, ISomeService someService) : base(someService)
{
_aboutService = aboutService;
}
}
If you dont want to use that service you use the default base constructor like this otherwise it will be null if you try to access it:
public class AboutController : BaseController
{
private readonly IAboutService _aboutService;
public AboutController (IAboutService aboutService) : base()
{
_aboutService = aboutService;
}
}
You are in the first scenario so you need to pass it through the constructor in order to initialize it!
But if you like to break the pattern you would choose other ways of injecting the services into your base controller:
First one by using auto-properties:
For example:
public ISomeService SomeService { get; set;}
Another way is to get the instance of the service by using DependecyResolver but this would 'break' somehow the DI pattern and it will make harder for testing etc. But if you choose to do so here is the code for that:
public class BaseController : Controller
{
[Inject]
public IUnitOfWork UnitOfWork { get; set; }
private readonly ISomeService _someService ;
public BaseController(ISomeService someService)
{
_someService = someService;
}
public void Contacts()
{
contacts = _someService .GetById(1);
ViewBag.someThing = contacts; //Add whatever
}
public BaseController()
{
_someService = DependencyResolver.Current.GetService<ISomeService >();
}
}
For more info refer to this question here
Related
I have 2 interfaces:
public interface IPedidoService
{
UsuarioDrogueria CUsuarioDrogueria(string userId, int idDrogueria);
List<PedidoComboProducto> CPedidosCombosProductos(int idcombo, int idPedido);
}
public interface IEmailService
{
void SendEmailAttachment(string email, string subject, string archive);
void SendNotificationEmail(List<Pedido> pedidos, string email, Drogueria drog);
void SendNotificationEmailADM(Pedido pedido) ;
}
I want to use the functions from IEmailService inside IPedidoService, so I inject it in its constructor when I create the respository.
public class PedidoService : IPedidoService
{
private readonly IEmailService emailService;
public PedidoService(IEmailService e)
{
this.emailService = e;
}
}
Up until here everything works fine, but when I try to do reverse the roles (IPedidoService functions inside IEmailService):
public class EmailService : IEmailService
{
private readonly IPedidoService pedidoSettings;
public EmailService(IPedidoService p)
{
this.pedidoSettings = p;
}
}
I end up getting this exception:
System.InvalidOperationException: A circular dependency was detected for the service of type
'EnvioPedidos.Data.Abstract.IPedidoService'.
EnvioPedidos.Data.Abstract.IPedidoService(EnvioPedidos.PedidoService) ->
EnvioPedidos.Data.Abstract.IEmailService(EnvioPedidos.EmailService) ->
EnvioPedidos.Data.Abstract.IPedidoService
Can anybody help me trace the issue here?
A simple way is to use Lazy<T> class which is based on this blog:
Custom extension method:
public static class LazyResolutionMiddlewareExtensions
{
public static IServiceCollection AddLazyResolution(this IServiceCollection services)
{
return services.AddTransient(
typeof(Lazy<>),
typeof(LazilyResolved<>));
}
}
public class LazilyResolved<T> : Lazy<T>
{
public LazilyResolved(IServiceProvider serviceProvider)
: base(serviceProvider.GetRequiredService<T>)
{
}
}
Configure in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
//services.AddSingleton<IPedidoService, PedidoService>();
//services.AddSingleton<IEmailService, EmailService>();
services.AddLazyResolution();
}
Change your implements class:
public class PedidoService : IPedidoService
{
private readonly Lazy<IEmailService> emailService;
public PedidoService(Lazy<IEmailService> e)
{
this.emailService = e;
}
//...
}
public class EmailService : IEmailService
{
private readonly Lazy<IPedidoService> pedidoSettings;
public EmailService(Lazy<IPedidoService> p)
{
this.pedidoSettings = p;
}
//...
}
When you have 2 classes, they cannot reference each other by dependency injection. This is called a circular dependency, as shown by your error. You need a 3rd class that references both services and you can use the methods there.
public class PedidoService
{
public PedidoService()
{
}
}
public class EmailService
{
public EmailService()
{
}
}
public class Container
{
private readonly EmailService emailService;
private readonly PedidoService pedidoService;
public Container(EmailService emailService, PedidoService pedidoService)
{
this.emailService = emailService;
this.pedidoService = pedidoService;
}
//use the services here
}
I'm trying to use Entity Framework Core with ASP.NET Boilerplate .NET Core, but I don't want to use Repository built-in functions.
There is a problem with my DB context; it keeps returning:
System.ArgumentNullException: 'Value cannot be null.'
for the DbContext instance as shown below:
public class MainProjectsAppService : ApplicationService
{
private readonly DecentralizationDbContext _ctx;
public MainProjectsAppService(IDbContextProvider<DecentralizationDbContext> dbContextProvider)
{
_ctx = dbContextProvider.GetDbContext();
}
public void CustomizedCreateMainProject(MainProject mainProject)
{
MainProject customizedMainProject = new MainProject
{
...
};
_ctx.MainProjects.Add(customizedMainProject);
_ctx.SaveChanges();
}
}
Below is the DbContext class code:
namespace Decentralization.EntityFrameworkCore
{
public class DecentralizationDbContext : AbpZeroDbContext<Tenant, Role, User, DecentralizationDbContext>
{
/* Define a DbSet for each entity of the application */
public DbSet<MainProject> MainProjects { get; set; }
public DecentralizationDbContext(DbContextOptions<DecentralizationDbContext> options)
: base(options)
{
}
}
}
Do not call dbContextProvider.GetDbContext() in the constructor.
Define a getter instead:
public class MainProjectsAppService : ApplicationService
{
private readonly IDbContextProvider<DecentralizationDbContext> _dbContextProvider;
private DecentralizationDbContext _ctx => _dbContextProvider.GetDbContext();
public MainProjectsAppService(IDbContextProvider<DecentralizationDbContext> dbContextProvider)
{
_dbContextProvider = dbContextProvider;
}
}
Reference: aspnetboilerplate/aspnetboilerplate#4809
I am using repository pattern on EF Core and Autofac in a windows service.
I have a service that needs to connect with the some dozen databases which have the same schema (same dbcontext) but only different data.
How can I achieve this in my service using Autofac? Belo
public class ReportRepository : IReportRepository
{
private readonly ReportDbContext dbContext;
public ReportRepository(ReportDbContext dbContext)
{
this.dbContext = dbContext
}
public SomeModel GetData()
{
return dbContext.SalesData;
}
}
public class ReportService : IReportService
{
private readonly IReportRepository reportRepositoryEUServer;
public ReportService(IReportRepository reportRepositoryEUServer)
{
this.reportRepositoryEUServer = reportRepositoryEUServer
}
public SomeModelDto GenerateReport()
{
var euData = reportRepositoryEUServer.GetData();
// I need to call other servers (e.g LATAM) here and get the data and aggregate them with euData
}
}
Create base context including all settings, dbsets etc:
public abstract class BaseContext : DbContext
{
public BaseContext(DbContextOptions options)
: base(options)
{ }
public DbSet<object> FirstSet { get; set; }
...
}
inherit from BaseContext for both DBs
public class LATAMContext : BaseContext
{
public LATAMContext(DbContextOptions<LATAMContext> options) : base(options)
{
}
}
public class EUContext : BaseContext
{
public EUContext(DbContextOptions<EUContext> options) : base(options)
{
}
}
and register both in Startup.cs
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddDbContext<LATAMContext>(options => options.UseSqlServer(Configuration.GetConnectionString("LATAMConnectionString")));
services.AddDbContext<EUContext>(options => options.UseSqlServer(Configuration.GetConnectionString("EUConnectionString")));
// Autofac
var builder = new ContainerBuilder();
// needed only if you plan to inject ICollection<BaseContext>
builder.RegisterType<LATAMContext>().As<BaseContext>();
builder.RegisterType<EUContext>().As<BaseContext>();
builder.Populate(services);
return new AutofacServiceProvider(builder.Build());
}
add connection strings in appsettings.json
"ConnectionStrings": {
"LATAMConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true",
"EUConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
}
and now you can inject both contexts
public class ReportRepository : IReportRepository
{
private readonly LATAMContext latamDbContext;
private readonly EUContext euDbContext;
public ReportRepository(LATAMContext latamDbContext, EUContext euDbContext)
{
this.latamDbContext = latamDbContext;
this.euDbContext = euDbContext;
}
}
or if you plan to inject collection of contexts
public class ReportRepository : IReportRepository
{
private readonly ICollection<BaseContext> dbContexts;
public ReportRepository(ICollection<BaseContext> dbContexts)
{
this.dbContexts = dbContexts;
}
}
to access specific context
var _euContext = dbContexts.FirstOrDefault(x => x is EUContext) as EUContext;
var _latamContext = dbContexts.FirstOrDefault(x => x is LATAMContext) as LATAMContext;
Given the following ASP.NET Core controller :
public class MyController : Controller {
public MyController(IDependency dependency) { this.dependency = dependency; }
}
public interface IDependency;
public class DependencyImplementation : IDependency {
public DependencyImplementation(Controller controller) { ... }
}
I want MyController to have a new instance of DependencyImplementation injected, constructed with the controller it's being passed to. Ideally using Ninject.
The non-IoC version would be:
public class MyController : Controller {
public MyController() { this.dependency = new DependencyImplementation(this); }
}
This would cause circular dependency. Only idea I have is to introduce factory:
public interface IDependencyFactory
{
IDependency Create(Controller controller);
}
public class MyController : Controller
{
private IDependency dependency;
public MyController(IDependencyFactory dependencyFactory)
{
this.dependency = dependencyFactory.Create(this);
}
}
var kernel = new StandardKernel();
kernel.Bind<Controller>().To<MyController>();
kernel.Bind<IDependency>().To<DependencyImplementation>();
kernel.Bind<IDependencyFactory>().ToFactory();
var controller = kernel.Get<Controller>();
Or maybe rather reconsider whole design.
I have classes such as AccountsController, ProductsController etc that all inherit from BaseController. Unity sets up my services as needed. These classes also all require a _sequence service. As it is a common requirement for all classes I would like to code this in the BaseController.
public class AccountsController : BaseController
{
public AccountsController(
IService<Account> accountService) {
_account = accountService;
}
public class ProductsController : BaseController
{
public ProductsController(
IService<Account> productService) {
_product = productService;
}
public class BaseController : Controller
{
public IService<Account> _account;
public IService<Product> _product;
protected ISequenceService _sequence;
public BaseController(
ISequenceService sequenceService) {
_sequence = sequenceService;
}
But how can I do this? Should I set up a call to the BaseController inside the constructors of each of the AccountsController and ProductsController?
You can chain constructors:
public class ProductsController : BaseController
{
public ProductsController(
IService<Account> productService) : base(productService)
{
_product = productService;
}
}
Note that the chained BaseController (using the base keyword) has been passed the productService parameter, tough this can be anything.
Update:
You could do the following (poor mans' dependency injection):
public class ProductsController : BaseController
{
public ProductsController(
IService<Account> productService) : base(new SequenceService())
{
_product = productService;
}
}
Or, pass in the dependency on ISequenceService through your inheriting types:
public class ProductsController : BaseController
{
public ProductsController(
IService<Account> productService, ISequenceService sequenceService)
: base(sequenceService)
{
_product = productService;
}
}