I need some help to understand how to instantiate ViewModels without having all of them as parameters in the MainViewModel Class constructor.
Could any of you help me to get my head around and get rid of so many parameters in the constructor. I've read about FactoryPatterns but I don't understand how to implement it or maybe that is not the solution?. Anyway here's the code.
Thank you. Pls help me this is driving me nuts!
app.xaml.cs
private readonly ServiceProvider _serviceProvider;
public App()
{
ServiceCollection services = new ServiceCollection();
ConfigureServices(services);
_serviceProvider = services.BuildServiceProvider();
}
private void ConfigureServices(ServiceCollection services)
{
services.AddSingleton<MainWindow>();
// Services
services.AddSingleton<ICustomerService, CustomerService>();
// ViewModels
services.AddScoped<MainViewModel>();
services.AddScoped<CustomerViewModel>();
services.AddScoped<CustomerAddViewModel>();
services.AddScoped<CustomerEditViewModel>();
services.AddScoped<ServiceViewModel>();
}
private void OnStartup(object sender, StartupEventArgs e)
{
var mainWindow = _serviceProvider.GetService<MainWindow>();
mainWindow.DataContext = _serviceProvider.GetService<MainViewModel>();
mainWindow.Show();
}
MainViewMode.cs
public class MainViewModel : ViewModelBase
{
private CustomerViewModel _customerViewModel;
private CustomerAddViewModel _customerAddViewModel;
private CustomerEditViewModel _customerEditViewModel;
private ViewModelBase _selectedViewModel;
public ViewModelBase SelectedViewModel
{
get => _selectedViewModel;
set
{
_selectedViewModel = value;
NotifyPropertyChanged();
}
}
public RelayCommand CustCommand { get; set; }
public RelayCommand ServCommand { get; set; }
**public MainViewModel(
CustomerViewModel customerViewModel,
CustomerAddViewModel customerAddViewModel,
CustomerEditViewModel customerEditViewModel)
{
_customerViewModel = customerViewModel;
_customerAddViewModel = customerAddViewModel;
_customerEditViewModel = customerEditViewModel;
CustCommand = new RelayCommand(OpenCustomer);
}**
private void OpenCustomer()
{
SelectedViewModel = _customerViewModel;
}
}
CustomerViewModel
public class CustomerViewModel : ViewModelBase
{
private ICustomerService _repo;
private ObservableCollection<Customer> _customers;
public ObservableCollection<Customer> Customers
{
get => _customers;
set
{
_customers = value;
NotifyPropertyChanged();
}
}
public CustomerViewModel(ICustomerService repo)
{
_repo = repo;
}
public async void LoadCustomers()
{
List<Customer> customers = await _repo.GetCustomers();
Customers = new ObservableCollection<Customer>(customers);
}
}
You're using constructor injection, which is only one of the ways to do DI. Another method is property injection, where you typically do something like this:
public class ClassToBeInjected
{
[Inject]
public SomeDataType PropertyToBeInjected {get; set;}
... etc ...
}
So long as ClassToBeInjected is being created via the DI framework, any properties tagged with [Inject] will also be injected automatically. Furthermore, any properties of SomeDataType that are tagged with the [Inject] attribute will also be injected, and so on down the heirarchy.
The exact mechanism of how this is achieved will depend on your DI framework. I've used the [Inject] attribute here, which is what Ninject uses, but each framework will have its own way of doing it.
You don't need to pass all your dependencies to MainViewModel, now it's easy because you only have three, what if they were 20? I think the best thing to do here is to inject the dependency container and get all your view models from there.
public class MainViewModel : ViewModelBase
{
private ServiceProvider _serviceProvider;
public MainViewModel(IServiceProvider provider)
{
_serviceProvider = provider;
}
private void SomeMethod()
{
CustomerViewModel viewModel = _serviceProvider.GetRequiredService<CustomerViewModel>();
}
}
Related
I would like to inject my .NET Core EntityFramework DbContext (sitting in a .net standard library) into my WPF app.
I tried this Unity approach:
OnStartup
var container = new UnityContainer();
container.RegisterType<ApplicationDbContext>();
var mainWindow = container.Resolve<MainWindow>();
base.OnStartup(e);
MainWindow
private ApplicationDbContext _db;
[Dependency]
public ApplicationDbContext Db
{
get
{
return _db;
}
set
{
_db = value;
}
}
public MainWindow()
{
//StandardDatabase.Commands.Test();
InitializeComponent();
DataContext = this;
FrameContent.Navigate(new PageConsignments());
}
But I get this error at container.Resolve<MainWindow>():
The current type, System.Collections.Generic.IReadOnlyDictionary`2[System.Type,Microsoft.EntityFrameworkCore.Infrastructure.IDbContextOptionsExtension], is an interface and cannot be constructed. Are you missing a type mapping?
Does anyone know if I'm doing something wrong? Any suggestions on a better way of doing this are welcome
ApplicationDbContext
public ApplicationDbContext() : base() { }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{ }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseLazyLoadingProxies()
.UseSqlServer("Server=L-TO-THE-APTOP\\SQLEXPRESS;Database=Maloli;Trusted_Connection=True;MultipleActiveResultSets=true");
optionsBuilder.ConfigureWarnings(x => x.Ignore(CoreEventId.LazyLoadOnDisposedContextWarning));
}
As per Nkosi's suggestion, I removed the ApplicationDbContext(options) ctor from the context, and that got rid of the error.However I am now checking the value of Db here in MainWindow:
private ICommand goPack;
public ICommand GoPack
{
get
{
return goPack
?? (goPack = new ActionCommand(() =>
{
var c = _db.Parts;
FrameContent.Navigate(new PageConsignments());
}));
}
}
But it returns null
The original error was because the container was selecting the constructor that expected DbContextOptionsBuilder which the conateinr did not know how to resolve properly.
Since the context is being configured within the OnConfiguring override then there is no need for
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{ }
Remove that constructor so the container resolve the context without errors.
Depending on the flow of dependency initialization and access to it, that context should really be explicitly injected into a view model and not directly on the View.
Following MVVM, have all the necessary dependencies and bindable properties in the view model
public class MainWindowViewModel : BaseViewModel {
private readonly ApplicationDbContext db;
public MainWindowViewModel(ApplicationDbContext db) {
this.db = db;
}
private ICommand goPack;
public ICommand GoPack {
get {
return goPack
?? (goPack = new ActionCommand(() =>
{
var c = db.Parts;
FrameContent.Navigate(new PageConsignments());
}));
}
}
}
Update the View to depend on the view model
public class MainWindow : Window {
[Dependency]
public MainWindowViewModel ViewModel {
set { DataContext = value; }
}
public MainWindow() {
InitializeComponent();
Loaded += OnLoaded;
}
void OnLoaded(object sender, EventArgs args) {
FrameContent.Navigate(new PageConsignments());
}
}
All that is left now is to make sure all dependencies are registered with container
public class App : Application {
protected override void OnStartup(StartupEventArgs e) {
IUnityContainer container = new UnityContainer();
container.RegisterType<ApplicationDbContext>();
container.RegisterType<MainWindowViewModel>();
container.RegisterType<MainWindow>();
MainWindow mainWindow = container.Resolve<MainWindow>();
mainWindow.Show();
}
}
Where ever possible, The Explicit Dependencies Principle via constructor injection should be preferred over property injection.
But since most views do not lend well to constructor injection the latter is usually applied. By making sure the view model has all the necessary dependencies before injecting it into the view you ensure that all required values are available when needed.
NotificationHubConnectionSettings.cs file to fetch connection string from web.config
public class NotificationHubConnectionSettings
{
public NotificationHubClient Hub { get; set; }
public NotificationHubConnectionSettings()
{
Hub = NotificationHubClient.CreateClientFromConnectionString(ConfigurationManager.AppSettings["Microsoft.Azure.NotificationHubs.ConnectionString"], ConfigurationManager.AppSettings["NotificationHub"]);
}
}
Inside Bootstrapper.cs
using Unity dependency injection nuget
private static IUnityContainer BuildUnityContainer()
{
try
{
var container = new UnityContainer();
container.RegisterType<NotificationHubConnectionSettings>().RegisterType<NotificationHubConnectionSettings>(new HierarchicalLifetimeManager());
return container;
}
catch (Exception)
{
return null;
}
}
In HomeController.cs
want to implement dependency injection -
private readonly NotificationHubClient _hub;
public HomeController(NotificationHubConnectionSettings hub)
{
_hub = hub.Hub;
}
// POST api/register
// This creates a registration id
public async Task<string> Post(string handle = null)
{
string newRegistrationId = null;
if (handle != null)
{
var registrations = await _hub.GetRegistrationsByChannelAsync(handle, 100);
Is this correct way to implement dependency injection?
You should be using a interface type here.
Create a contract for INotificationHubConnectionSettings class in the form of a interface which dictates to your system all public methods and properties available.
public interface INotificationHubConnectionSettings
{
NotificationHubClient Hub { get; set; }
}
Then have your actual NotificationHubConnectionSettings class inherit from this interface;
public class NotificationHubConnectionSettings : INotificationHubConnectionSettings
{
public NotificationHubClient Hub { get; set; }
public NotificationHubConnectionSettings()
{
Hub = NotificationHubClient.CreateClientFromConnectionString(ConfigurationManager.AppSettings["Microsoft.Azure.NotificationHubs.ConnectionString"], ConfigurationManager.AppSettings["NotificationHub"]);
}
}
Now register the interface and class inside of UnityContainer and change your constructor to the following;
private readonly INotificationHubClient _hub;
public HomeController(NotificationHubConnectionSettings hub)
{
_hub = hub.Hub;
}
Always use interfaces for dependency injection.
I have the next problem, i dont understand why this code dont work i think is because i dont injectate the class of constructor by autofac but i dont know how do that, can us help me to do that the better way?
Before I add the generator this work if i comment the generator code in service work.
This is my code:
I have a class Controller that invoke a serv:
public class ZonesController : Controller
{
private IZoneService zoneService;
public ZonesController(IZoneService zoneService)
{
this.zoneService = zoneService;
}
[HttpGet]
//Do work
}
This is the service and interface:
public class ZoneService : IZoneService
{
private readonly IZoneRepository zoneRepository;
private readonly IDtoFactory dtoFactory;
private readonly ZoneGenerator zoneGenerator;
public ZoneService(IZoneRepository zoneRepository,
IDtoFactory dtoFactory,
ZoneGenerator zoneGenerator)
{
this.zoneRepository = zoneRepository;
this.dtoFactory = dtoFactory;
this.zoneGenerator = zoneGenerator;
}
public void Add(ZoneDetailDTO zone)
{
zoneGenerator.Generate(zone);
}
//Do Work
}
public interface IZoneService
{
void Add(ZoneDetailDTO zone);
//Do Methods
}
The generator invoke ohter class, factories:
public class ZoneGenerator
{
private readonly ZoneFactory zoneFactory;
private readonly IZoneRepository zoneRepository;
public ZoneGenerator(ZoneFactory zoneFactory, IZoneRepository zoneRepository)
{
this.zoneFactory = zoneFactory;
this.zoneRepository = zoneRepository;
}
public void Generate(ZoneDetailDTO zoneModel)
{
var zone = zoneFactory.Create(zoneModel);
zoneRepository.Add(zone);
}
}
The Factory:
public class ZoneFactory
{
private readonly ZoneMapFactory zoneMapFactory;
private readonly ZoneScheduleFactory zoneScheduleFactory;
public ZoneFactory(ZoneMapFactory zoneMapFactory,
ZoneScheduleFactory zoneScheduleFactory)
{
this.zoneMapFactory = zoneMapFactory;
this.zoneScheduleFactory = zoneScheduleFactory;
}
public Zone Create(zoneDetailDTO zone)
{
var map = zoneMapFactory.Create(zone.Map.Address, zone.Map.Latitude, zone.Map.Longitude);
var schedule = zoneScheduleFactory.Create(zone.Schedule.StartHour, zone.Schedule.EndHour);
return new Zone(zone.Name,
zone.ProvinceId,
map,
schedule,
zone.Tags);
}
}
And finally my container:
//method in Startup class Asp.Net Core
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddSingleton(_ => Configuration);
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterModule<DefaultModule>();
containerBuilder.Populate(services);
var container = containerBuilder.Build();
return new AutofacServiceProvider(container);
}
public class DefaultModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ZoneService>().As<IZoneService>();
builder.RegisterType<ZoneRepository>().As<IZoneRepository>();
builder.RegisterType<ProvinceService>().As<IProvinceService>();
builder.RegisterType<ProvinceRepository>().As<IProvinceRepository>();
builder.RegisterType<DtoFactory>().As<IDtoFactory>();
}
}
You have missed to add to your Load method the following:
builder.RegisterType<ZoneGenerator>().AsSelf();
builder.RegisterType<ZoneFactory>().AsSelf();
builder.RegisterType<ZoneMapFactory>().AsSelf();
builder.RegisterType<ZoneScheduleFactory>().AsSelf();
I am starting to use Ninject in my MVC5 code-first app. Here's my NinjectWebCommon.cs:
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
try
{
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
kernel.Bind<CMSContext>()
.ToSelf()
//.InSingletonScope();
.InRequestScope();
kernel.Bind<IExecutiveRepository>()
.To<ExecutiveRepository>();
kernel.Bind<IExecutiveSectionRepository>()
.To<ExecutiveSectionRepository>();
kernel.Bind<IExecutiveSectionMappingRepository>()
.To<ExecutiveSectionMappingRepository>();
kernel.Bind<IUserRepository>()
.To<UserRepository>();
kernel.Bind<IContentRepository>()
.To<ContentRepository>();
RegisterServices(kernel);
return kernel;
}
catch
{
kernel.Dispose();
throw;
}
}
I tried .InSingletonScope() as well as .InRequestScope() but I still get the 'entity object cannot be referenced by multiple instances of IEntityChangeTracker' error.
Here is my Interface:
public interface IExecutiveRepository : IDisposable
{
IEnumerable<Executive> GetExecutives();
Executive GetExecutiveById(int executiveId);
void InsertExecutive(Executive executive);
void UpdateExecutive(Executive executive);
void DeleteExecutive(int executiveId);
void Save();
}
Here is my concrete:
public class ExecutiveRepository : IExecutiveRepository, IDisposable
{
private CMSContext context;
public ExecutiveRepository(CMSContext context)
{
this.context = context;
}
public IEnumerable<Executive> GetExecutives()
{
return context.Executives.ToList();
}
public Executive GetExecutiveById(int id)
{
return context.Executives.Find(id);
}
public void InsertExecutive(Executive executive)
{
context.Executives.Add(executive);
}
public void DeleteExecutive(int executiveId)
{
Executive executive = context.Executives.Find(executiveId);
context.Executives.Remove(executive);
}
public void UpdateExecutive(Executive executive)
{
context.Entry(executive).State = EntityState.Modified;
}
public void Save()
{
context.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
context.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
Here is the controller(top pertinent part):
public class ExecutiveController : Controller
{
private IExecutiveRepository executiveRepository;
private IUserRepository userRepository;
private IExecutiveSectionRepository executiveSectionRepository;
private IExecutiveSectionMappingRepository executiveSectionMappingRepository;
private IContentRepository contentRepository;
private Ninject.IKernel _kernel = new StandardKernel();
//[Inject]
public ExecutiveController()
{
executiveRepository = _kernel.Get<ExecutiveRepository>();
userRepository = _kernel.Get<UserRepository>();
executiveSectionRepository = _kernel.Get<ExecutiveSectionRepository>();
executiveSectionMappingRepository = _kernel.Get<ExecutiveSectionMappingRepository>();
contentRepository = _kernel.Get<ContentRepository>();
}
...
Not sure what I am doing wrong but upon adding a new 'Executive' it bombs... I do understand it's trying to use separate contexts and that's the problem, but I 'm just not sure how to fix it. Apparently, the line in the NinjectWebCommon.cs class:
kernel.Bind<CMSContext>()
.ToSelf()
//.InSingletonScope();
.InRequestScope();
Is supposed to be the fix, but it isn't...
any ideas/suggestions?
You should be using NUGET package Ninject.Web.Mvc if you aren't already. This configures your application ready to use Ninject, other than your bindings. It looks like you are reasonably familiar with the bindings side of things already from what I can see in your CreateKernel() method.
Once your bindings are in place, you should not be creating Kernels in your controllers, this is because the Ninject.Web.Mvc library configures Ninject to create your controllers for you under the hood. Therefore any dependencies that you add to them should be automatically resolved.
So, you can use constructor injection to resolve your dependencies:
public class ExecutiveController : Controller
{
private IExecutiveRepository ExecutiveRepository;
private IUserRepository UserRepository;
private IExecutiveSectionRepository ExecutiveSectionRepository;
private IExecutiveSectionMappingRepository ExecutiveSectionMappingRepository;
private IContentRepository ContentRepository;
public ExecutiveController(
IExecutiveRepository executiveRepository,
IUserRepository userRepository,
IExecutiveSectionRepository executiveSectionRepository,
IExecutiveSectionMappingRepository executiveSectionMappingRepository,
IContentRepository contentRepository)
{
// Set the field values
this.ExecutiveRepository = executiveRepository,
this.UserRepository = userRepository,
this.ExecutiveSectionRepository = executiveSectionRepository,
this.ExecutiveSectionMappingRepository = executiveSectionMappingRepository,
this.ContentRepository = contentRepository;
}
public ActionResult Index(int id)
{
// Use one of your dependencies...
var executive = this.executiveRepository.GetExecutiveById(id);
}
}
Or you can use the [Inject] attribute which has the same effect:
public class ExecutiveController : Controller
{
[Inject]
public IExecutiveRepository executiveRepository { get; set; }
[Inject]
public IUserRepository userRepository { get; set; }
[Inject]
public IExecutiveSectionRepository executiveSectionRepository { get; set; }
[Inject]
public IExecutiveSectionMappingRepository executiveSectionMappingRepository { get; set; }
[Inject]
public IContentRepository contentRepository { get; set; }
public ExecutiveController()
{
}
public ActionResult Index(int id)
{
// Use one of your dependencies...
var executive = this.executiveRepository.GetExecutiveById(id);
}
}
You're creating a kernel per controller.
InRequestScope only ensures one instance per request per kernel.
So you need to adapt your setup of the kernel so there's only one kernel per web application. See:
Ninject.Web.Mvc
Tutorial
Youtube
This may not answer the question. But I tend to use the IDbContextFactory that EF provides you with and do something like this:
public interface IDefaultContextFactory : IDbContextFactory<CMSContext> {}
public class DefaultContextFactory : IDefaultContextFactory
{
private readonly Lazy<CMSContext> lazyContext = new Lazy<CMSContext>(() => new CMSContext());
public CMSContext Create()
{
return lazyContext.Value;
}
}
Then you just bind that, and when you need the context you can do something like this:
public class ExecutiveRepository : IExecutiveRepository, IDisposable
{
private readonly CMSContext context;
public ExecutiveRepository(IDefaultContextFactory contextFactory)
{
this.context = contextFactory.Create();
}
}
I believe #BatteryBackupUnit is correct, I would also consider using the above pattern for contexts.
I would like to implement CastleWindsor with the MVP pattern, but I keep getting an 'Object Reference Not Set to an Object reference on the Presenter when the repository is called to obtain some data.
This is how I did it and I am wondering if there is anything wrong, so please let me know if you can:
Presenter:
public class CategoryPresenter
{
ICategoryRepository categoryRepository;
ICategoryView categoryView;
public CategoryPresenter(ICategoryView _categoryView, ICategoryRepository _categoryRepository)
{
categoryView = _categoryView;
categoryRepository = _categoryRepository;
}
//public CategoryPresenter(ICategoryView _categoryView) : this (_categoryView, new CategoryRepository())
//{ }
public CategoryPresenter(ICategoryView _view)
{
categoryView = _view;
}
public IEnumerable<object> GetActiveCategories()
{
return categoryRepository.GetActiveCategories();
}
}
IoC Class:
public static class IoC
{
public static IWindsorContainer windsorContainter { get; set; }
}
IoCConfig Class:
class IoCConfig
{
public static IWindsorContainer RegisterCastleWindsorContainer()
{
IWindsorContainer windsorContainer = new WindsorContainer()
.Install(new RepositoryInstaller())
IoC.windsorContainter = windsorContainer;
return windsorContainer;
}
}
Installer Class:
public class RepositoryInstaller: IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Component.For<ICategoryRepository>().ImplementedBy<CategoryRepository>).LifestyleTransient());
}
}
Finally in Global.ascx file I am doing this at App_Start:
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
IoCConfig.RegisterCastleWindsorContainer();
}
With this, the error message is as said above; the error happens at the presenter's method: GetActiveCategories();
As you see at no where in code I invoke the resolve method on the container.
Please let me know if if you have any suggestions.
Thank you.
I have resolved this to the IoC Class
public static T Resolve<T>()
{
try
{
return windsorContainer.Resolve<T>();
}
catch (Exception e)
{
throw e;
}
}
And then add this to the presenter:
ICategoryRepository categoryRepository = IoC.Resolve<ICategoryRepository>();
ICategoryView categoryView = IoC.Resolve<ICategoryView>();