I am going to make up an example here just to get my point across. Please consider the following class:
public class MovieController : Controller
{
private readonly IMovieService _movieService;
private readonly IUserService _userService;
public MovieController(IMovieService movieService, IUserService userService)
{
_movieService = movieService;
_userService = userService;
}
public ViewModel GetMovies()
{
return View("Movies", _movieService.GetMovies());
}
public ViewModel GetAuthors()
{
return View("Authors", _userService.GetAuthors());
}
}
With the example above, whenever the MovieController is created, it will create both services. Each service will require its services and repositories in the constructor. So, in reality, I may be creating some classes each time MovieController is called. For this reason, I want to implement Lazy loading as I believe it will improve performance. For this reason, please consider the next class:
public class MovieController : Controller
{
private readonly IMovieService _movieService;
private readonly IUserService _userService;
private MovieService
{
get
{
if (_movieService == null) _movieService = new MovieService();
return _movieService;
}
}
private UserService
{
get
{
if (_userService == null) _userService = new UserService();
return _userService;
}
}
public MovieController() { }
public ViewModel GetMovies()
{
return View("Movies", MovieService.GetMovies());
}
public ViewModel GetAuthors()
{
return View("Authors", UserService.GetAuthors());
}
}
The problem with the above example is that I lost DI. Now I understand the benefit of DI, and I very much want to keep it, and as such, I came up with the following example:
public class MovieController : Controller
{
private readonly IMovieService _movieService;
private readonly IUserService _userService;
private MovieService
{
get
{
if (_movieService == null) _movieService = DependencyResolver.Current.GetService(typeof(IMovieService));
return _movieService;
}
}
private UserService
{
get
{
if (_userService == null) _userService = DependencyResolver.Current.GetService(typeof(IUserService));
return _userService;
}
}
public MovieController() { }
public ViewModel GetMovies()
{
return View("Movies", MovieService.GetMovies());
}
public ViewModel GetAuthors()
{
return View("Authors", UserService.GetAuthors());
}
}
Is my third example in this question a bad practice? If so, why? Am I losing on performance by doing this or is there another reason why this would be considered bad practice?
Using the dependency resolver this way means that you are using the service locator anti-pattern.
Is there any reason why you think that creating MovieService and UserService will have performance issues? Are you doing something significant in the constructor of these classes? If so, then you probably shouldn't. In the constructor you should almost always just accept the dependencies and store them in private fields.
If for some reason, you still want lazy loading, then you can create something like this:
public class LazyMovieService : IMovieService
{
private readonly Lazy<IMovieService> lazyInstance;
public LazyMovieService(Func<IMovieService> instanceFactory)
{
lazyInstance = new Lazy<IMovieService>(instanceFactory);
}
public string[] GetMovies()
{
return lazyInstance.Value.GetMovies();
}
}
This class allows you to use the first code example in your code while still being able to lazily load your services.
In the Composition Root, you would simply do something like:
var controller =
new MovieController(
new LazyMovieService(() => new MyOriginalMovieService(...)),
...);
Related
How can I inject a specific setting (of possibly many) from an array appSettings.json in a C# .NET Core Web API, based on a runtime input value?
appSettings.json:
{
"SettingProfiles": [
{
"Name": "Profile1",
"SettingA": "SettingAValue1",
"SettingB": "SettingBValue1"
},
{
"Name": "Profile2",
"SettingA": "SettingAValue2",
"SettingB": "SettingBValue2"
}
...
}
Settings Classes:
public class Settings {
public List<SettingsProfile> SettingsProfiles { get; set; }
}
public class SettingsProfile {
public string Name { get; set; };
public string SettingA { get; set; };
public string SettingB { get; set; };
}
Service class:
public class MyService : IMyService {
private readonly SettingsProfile _Profile;
public MyService(SettingsProfile profile) {
_Profile = profile;
}
public void DoStuff() {
Console.WriteLine($"Setting A: {_SettingsProfile.SettingA}, Setting B: {_SettingsProfile.SettingB}")
}
}
The user will enter the setting name they want to apply. I am unsure how to do this if the service is configured in Startup.cs, at which point I don't yet have the setting to use.
I am understanding that "newing" the service would be bad practice, although that's the only way I can figure out how to make it work:
public class MyController {
private readonly Settings _Settings;
public MyController(Settings settings) {
_Settings = settings;
}
public IActionResult DoStuff(profileName) {
SettingsProfile profile = _Settings.Where(profile => profile.Name == profileName);
MyService service = new Service(profile);
}
}
I'm obviously missing something, but I've been watching YouTube videos on Dependency Injections and reading StackOverflow until my eyes bleed, and haven't figured it out yet. Can someone help me with a pattern that I should be following?
This is how I think it should work.
It will be a lot cleaner if you use another pattern: Factory.
interface ISettingServiceFactory{
MyService GetService(string profileName);
}
class SettingServiceFactory: ISettingServiceFactory
{
MyService GetService(string profileName){
}
}
Now you can implement GetService in two ways.
The first one is by creating new as you did in the controller and is not that bad as this is the purpose of the factory. In this way you kind of move that logic somewhere else.
A second one would be a bit uglier but something like this
interface ISettingServiceFactory{
MyService GetService(string profileName);
void SetCurrentProfile(SettingsProfile profile);
}
class SettingServiceFactory: ISettingServiceFactory
{
private IServiceProvider _serviceProvider;
private Settings _Settings;
public SettingServiceFactory(IServiceProvider serviceProvider,Settings settings){
_serviceProvider = serviceProvider;
_Settings = settings;
}
MyService GetService(string profileName){
var service = _serviceProvider.GetRequiredService<MyService>();
var profile = _Settings.Where(profile => profile.Name == profileName);
service.SetCurrentProfile(profile);
return service;
}
}
This second approach would be useful only if the implementation of MyService has a lot of other dependencies by itself and if you want to avoid new at any cost.
In both cases you will inject the factory in the controller
public MyController(ISettingServiceFactory settingServiceFactory) {
_settingServiceFactory= settingServiceFactory;
}
public IActionResult DoStuff(profileName) {
MyService service = _settingServiceFactory.GetService(profileName)
}
I have a customer view model which has drop down lists for selecting country, area and language. I am using ViewComponent to load the necessary data for the dropdowns and it's working like a charm. My problem is that when they are multiple client models on the page I am making multiple calls to the external API to receive the same data. I tried to put the Component.InvokeAsync part inside a cache tag helper but it also keeps the elements naming from the first call and messes up the model binding. Is there a way to avoid the multiple calls for same data?
Here is what the code looks like. It's nothing special. The views just bind the properties and there isn't anything special there.
[ViewComponent(Name = "LocationSelector")]
public class LocationSelector : ViewComponent
{
private readonly ILocationService locationsService;
public LocationSelector(ILocationService locationService)
{
this.locationsService = locationService;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var locationManager = new LocationSelectorViewModel();
locationManager.Areas = await this.locationsService.GetAreas();
locationManager.Countries = await this.locationsService.GetCountries();
return View("Default", locationManager);
}
}
And the component is called inside the customer model like this. The problem is that I may have multiple customers in my page and they all will make requests to the service for the exact same data.
#await Component.InvokeAsync(nameof(LocationSelector))
You should consider caching the data. You can use the IMemoryCache imeplemenation. I prefer to create my own abstraction layer which i would use it wherever i need.
public interface ICache
{
T Get<T>(string key, Func<T> updateExpression = null, int cacheDuration=0);
}
public class InMemoryCache : ICache
{
readonly IMemoryCache memoryCache;
public InMemoryCache(IMemoryCache memoryCache)
{
this.memoryCache = memoryCache;
}
public T Get<T>(string key, Func<T> updateExpression = null,int cacheDuration=0)
{
object result;
if (memoryCache.TryGetValue(key,out result))
{
return (T) result;
}
else
{
if (updateExpression == null)
{
return default(T);
}
var data = updateExpression();
if (data != null)
{
var options = new MemoryCacheEntryOptions
{
AbsoluteExpiration = DateTime.Now.AddSeconds(cacheDuration)
};
this.memoryCache.Set(key, data, options);
return data;
}
return default(T);
}
}
}
Now in your startup class's ConfigureServices method, enable caching and define our custom ICache-InMemoryCache mapping.
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ICache, InMemoryCache>();
services.AddMemoryCache();
}
Now you can inject ICache to any class and use it to get/store data to it.
public class LocationSelector : ViewComponent
{
private readonly ILocationService locationsService;
private readonly ICache cache;
public LocationSelector(ILocationService locationService,ICache cache)
{
this.locationsService = locationService;
this.cache = cache;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var locationManager = new LocationSelectorViewModel();
var areas = await this.cache.Get(CacheKey.Statuses, () =>
this.locationsService.GetAreas(),360);
locationManager.Areas = areas;
return View("Default", locationManager);
}
}
I have a class
public class SessionService : ISessionService
{
public const string SessionUserKey = "UserViewModel";
private readonly IHttpContextAccessor _httpContextAccessor;
private ISession _session => _httpContextAccessor.HttpContext.Session;
public SessionService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public UserViewModel LoggedInUser
{
get
{
return _session.GetObjectFromJson<UserViewModel>(SessionUserKey);
}
set
{
_session.SetObjectAsJson(SessionUserKey, value);
}
}
public void Destroy()
{
_session.Clear();
}
}
An instance of this class is injected to my controller like this:
Startup.cs
services.AddScoped<ISessionService, SessionService>();
AccountController.cs
public class AccountController : Controller
{
private readonly IUserAccountService _accountService;
private readonly ISessionService _sessionService;
public AccountController(IUserAccountService accountService, ISessionService sessionService)
{
_accountService = accountService;
_sessionService = sessionService;
}
}
However, in my view I want to access the currently logged in user like this:
#if(SomeClass.LoggedInUser != null){
// code removed for brevity
}
How can I achieve this without making the LoggedInUser property of my SessionService class static. It seems like something needs to me made static to have an access like this. But I was wondering if there was a better way to accomplish this. And no, I don't want to use the ViewBag.
I am not using ASP.Net Identity
You should use #inject if you want to inject services in your views using DI:
#inject WebProject.Services.ISessionService CurrentSessionService
#if (CurrentSessionService.LoggedInUser != null)
{
// ...
}
Here is how my application makes a call to the database:
Web App -> Business Layer -> Data Layer
Everything is using dependency injection.
For example:
In the controller in my Web app I make a call like this:
await _manager.GetCustomers();
Which goes into my Business Layer:
public class CustomerManager : ICustomerManager
{
private ICustomerRepo _repository;
public CustomerManager(ICustomerRepo repository)
{
_repository = repository;
}
public Task<IList<Customer>> GetCustomers(string name = null)
{
return _repository.GetCustomers(name);
}
}
Which goes into my Data Layer:
public class CustomerRepo : BaseRepo, ICustomerRepo
{
public CustomerRepo(IConfigurationRoot configRoot)
: base(configRoot)
{
}
public Customer Find(int id)
{
using (var connection = GetOpenConnection())
{
...
}
}
}
The trick here is that CustomerRepo inherits from BaseRepo to be able to use the GetOpenConnection() function. But at the same time BaseRepo needs an IConfigurationRoot injected into it from the web application. How can I do both?
public class BaseRepo
{
private readonly IConfigurationRoot config;
public BaseRepo(IConfigurationRoot config)
{
this.config = config;
}
public SqlConnection GetOpenConnection(bool mars = false)
{
string cs = config.GetSection("Data:DefaultConnection:ConnectionString").ToString();
...
}
}
How would you instantiate (or even compile) a CustomerRepo at all, regardless of dependency injection? You need an IConfigurationRoot parameter to pass through to the base constructor. Like:
public CustomerRepo(IConfigurationRoot configRoot)
: base(configRoot)
{
}
See https://msdn.microsoft.com/en-us/library/hfw7t1ce.aspx for info on the base keyword.
I'm new to Ninject so I'm sure that it's something I'm doing wrong, I'm just not sure what. I'm using Ninject and Ninject.MVC3 in my MVC3 web application. Here's an example of what I'm trying to do.
I'm using the Repository pattern:
public interface IRepository<T>
{
T Get(object id);
IList<T> GetAll();
void Add(T value);
void Update(T value);
void Delete(T value);
}
For a concrete type:
public Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public Customer()
{
}
}
Now I have 2 separate repositories, a cached version that needs injection to the database repository:
public CachedCustomerRepository : IRepository<Customer>
{
private IRepository<Customer> _repository;
public Customer Get(object id)
{
Customer cust = new Customer();
IList<Customer> custs = GetAll();
if (custs != null && custs.Count > 0)
cust = custs.FirstOrDefault(c => c.Id == int.Parse(id.ToString()));
return cust;
}
public IList<Customer> GetAll()
{
IList<Customer> custs = HttpRuntime.Cache["Customers"] as IList<Customer>;
if (custs == null)
{
custs = _repository.GetAll();
if (custs != null && custs.Count() > 0)
{
double timeout = 600000d;
HttpRuntime.Cache.Insert("Customers", custs, null, DateTime.UtcNow.AddMilliseconds(timeout), System.Web.Caching.Cache.NoSlidingExpiration);
}
else
{
throw new NullReferenceException();
}
}
return custs;
}
public void Add(Customer value)
{
throw new NotImplementedException();
}
public void Update(Customer value)
{
throw new NotImplementedException();
}
public void Delete(Customer value)
{
throw new NotImplementedException();
}
public CachedCustomerRepository()
{
}
[Inject]
public CachedCustomerRepository(IRepository<Customer> repository)
{
_repository = repository;
}
}
public class CustomerRepository : IRepository<Customer>
{
public Customer Get(object id)
{
Customer cust = new Customer();
IList<Customer> custs = GetAll();
if (custs != null && custs.Count > 0)
cust = custs.FirstOrDefault(c => c.Id == int.Parse(id.ToString()));
return cust;
}
public IList<Customer> GetAll()
{
//Customer retrieval code
}
public void Add(Customer value)
{
throw new NotImplementedException();
}
public void Update(Customer value)
{
throw new NotImplementedException();
}
public void Delete(Customer value)
{
throw new NotImplementedException();
}
public CachedCustomerRepository()
{
}
}
I set up a NinjectModule like this:
public class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<IRepository<Customer>>().To<CustomerRepository>();
}
}
and I modified the NinjectMVC3.cs in the AppStart folder to get the module when creating the kernel:
private static IKernel CreateKernel()
{
var kernel = new StandardKernel(new ServiceModule());
RegisterServices(kernel);
return kernel;
}
In my controller I am using this:
public ViewResult Index()
{
IRepository<Customer> custRepo = new CachedCustomerRepository();
return View(custRepo.GetAll());
}
It blows up on the line _repository.GetAll() in my CachedCustomerRepository.
I've set a breakpoint to make sure that the CreateKernel() is executing and getting the bindings, which it is. I'm just not sure why the injection isn't happening. One other side note, I don't know if it's important or not, the IRepository, the Repositories and the concrete type are in a separate class library and is referenced in the mvc3 web app. Both the web app and the class library have a reference to Ninject and the web app also has a reference to Ninject.MVC3. The binding and kernel creation is all taking place in the Web App.
First, you're calling the wrong constructor in your controller. This parameterless constructor doesn't call anything else and that's why you're getting the null exception. Second, you want to refactor your controller so that there are no direct dependencies.
You'd want to do something like the following:
public class SomeController
{
private readonly IRepository<Customer> repo;
public SomeController(IRepository<Customer> repo)
{
this.repo = repo;
}
public ViewResult Index()
{
return View(this.repo.GetAll());
}
}
This way, Ninject resolves the dependency for you! Ninject's kernel will be asked to create a controller with IRepository<Customer> by MVC. Since Ninject has this "binding", it'll try to instantiate the CustomerRepository for you.
Also, why are you creating a "CachedRepository"? I really, really think you're prematurely optimizing this. Honestly, you only need the one CustomerRepository and you'd wire that up within the Ninject module.
Have you made the class in Global.asax inherit NinjectHttpApplication?
Make sure you're calling DependencyResolver.SetResolver(CreateKernel()); in order to tell MVC which resolver you're going to use. You might be calling it but I didn't see it in your post - otherwise your code looks fine.
To get the full benefit of using an IOC you should refactor the CachedCustomerRepository dependency from you controller class. You will need to add a new binding to the your Ninject module. This binding will need to use the context to determine if it is binding the 'IRepository' to a CachedCustomerRepository instance or to the MVC Controller instance. Once you have that factored out, then Ninject will create and manage the lifetimes of both objects.
The problem is that when you create the CachedCustomerRepository in your action method, you're just instantiating it yourself and you're not getting Ninject to instantiate it for you and subsequently inject it's dependencies.
What you should do is use constructor injection for the controller.
E.g.
public class MyController : Controller
{
public IRepository<Customer> CustomerRepo { get; protected set; }
public MyController(IRepository<Customer> customerRepo)
{
CustomerRepo = customerRepo;
}
public ViewResult Index()
{
return View(CustomerRepo.GetAll());
}
}
Your scenario is slightly confusing though, because you are injecting a IRepository<Customer> into a IRepository<Customer> that needs to be injected into MyController.