Using DbContext with dependency injection - c#

I building WPF application in MVVM architecture. Pressing button should give me data from database on DataGrid. App correctly build and I can start it but when I press button I get "Object reference[...]" and information about dbContext was null.
Below some code:
AuctionDbContext.cs
public class AuctionDbContext: DbContext
{
public AuctionDbContext(DbContextOptions<AuctionDbContext> options): base(options)
{
/* Database.EnsureCreated();*/
}
public DbSet<Auction> Auctions { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
App.cs
public partial class App : Application
{
private ServiceProvider serviceProvider;
private DbCreator dbCreator = new DbCreator();
public App()
{
ServiceCollection services = new ServiceCollection();
services.AddDbContext<AuctionDbContext>(option =>
{
option.UseSqlite("Data Source = " + DbCreator.DATABASE_FILE_PATH);
});
services.AddSingleton<MainWindow>();
serviceProvider = services.BuildServiceProvider();
}
private void OnStartup(object sender, StartupEventArgs e)
{
dbCreator.createDbFile();
dbCreator.createConnectionToDatabase();
dbCreator.createTable();
dbCreator.fillTable();
var mainWindow = serviceProvider.GetService<MainWindow>();
mainWindow.Show();
}
}
}
MainWindow.cs
public partial class MainWindow : Window
{
AuctionDbContext dbContext;
public MainWindow()
{
InitializeComponent();
}
private void MarketMenu_Clicked(object sender, RoutedEventArgs e)
{
DataContext = new MarketViewModel(dbContext);
}
}
MarketViewModel.cs
public class MarketViewModel
{
AuctionDbContext dbContext;
MarketView marketView = new MarketView();
public MarketViewModel(AuctionDbContext dbContext)
{
this.dbContext = dbContext;
GetAuctions();
}
private void GetAuctions()
{
marketView.AuctionDG.ItemsSource = dbContext.Auctions.ToList(); /* Here I got error */
}
}
}
I used this doc and I do not see any mistake :( https://learn.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext
Before, when I had all in mainWindow class, everything was ok but that was PoC. Something went wrong, when I refactor project to MVVM. I spent a couple of hours looking for a solution but without success.
If it will help, here's my repo on GitHub https://github.com/BElluu/EUTool. Look at branch: 1-refactor-to-mvvm coz of master is outdated yet :)

You don't seem to initialize the dbContext field in the MainWindow:
public partial class MainWindow : Window
{
AuctionDbContext dbContext;
public MainWindow(AuctionDbContext dbContext)
{
this.dbContext = dbContext;
InitializeComponent();
}
private void MarketMenu_Clicked(object sender, RoutedEventArgs e)
{
DataContext = new MarketViewModel(dbContext);
}
}

Related

Unity Dependency is not working page to presenter in web forms

I am updating the old web forms project which is working on the MVP model. While refactoring that into the layered project I started DI using unity.
I have a below files with my new DI, but in my Presenter parameterized constructor is not called and ObjView is returning null. How can I fix this error
IUsersView
{
int UserID { get; set; }
List<UsersModel> oPTList { get; set; }
//Have only few methods
}
and i have page shown below
Users(.aspx.cs) : System.Web.UI.Page, IUsersView
{
UserPresenter obj = null; //Old code
IUserPresenter _userPresenter = null;
public readonly IUsersView _userView;
public readonly IUser _user;
public User(IUser user,
IUserPresenter userPresenter)
{
_user= user;
_userPresenter = userPresenter;
}
protected void Page_Load(object sender, EventArgs e)
{
try{
obj = new UserPresenter(this, _projectUpload); //Old Code
obj.CheckLoginUserExist(usrLoginID, usrName); //Old Code
_UserPresenter.CheckLoginUserExist(usrLoginID, usrName); // New Code with DI
}
}
}
and Presenter code
public class UserPresenter : IUserPresenter
{
IUserView objView;
IUser _user= null;
public UserPresenter()
{
}
public UserPresenter(IUserView view, IUser user)
{
objView = view;
_user = user;
}
public void CheckLoginUserExist(int usrLoginID, string usrName)
{
objView.oPTList = objBL.GetUserRoles(usrLoginID,usrName); //Old Code and i am getting date from DAL layer
objView.GetUserRoles(); //Old Code
}
}
try this, it looks like you have defined your interface incorrectly.
Users(.aspx.cs) : System.Web.UI.Page, IUsersView
{
IUserPresenter _userPresenter;
public readonly IUsersView _userView;
public readonly IUser _user;
public User(IUser user,
IUserPresenter userPresenter)
{
_User = user;
_userPresenter= userPresenter;
}
protected void Page_Load(object sender, EventArgs e)
{
try{
obj = new UserPresenter(this, _projectUpload); //Old Code
obj.CheckLoginUserExist(usrLoginID, usrName); //Old Code
_userPresenter.CheckLoginUserExist(usrLoginID, usrName); // New Code with DI
}
}
}

How to pass Unity container to child page from window for DI

I managed to set up DI using Unity:
App.xaml.cs
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
IUnityContainer container = new UnityContainer();
container.RegisterType<ApplicationDbContext>();
container.RegisterType<MainWindowViewModel>();
container.RegisterType<PageConsignmentsViewModel>();
container.RegisterType<MainWindow>();
container.RegisterType<PageConsignments>();
var mainWindow = container.Resolve<MainWindow>();
var pc = container.Resolve<PageConsignments>();
mainWindow.Show();
}
MainWindow.xaml.cs
public partial class MainWindow : Window
{
[Dependency]
public MainWindowViewModel ViewModel
{
set => DataContext = value;
}
public MainWindow()
{
InitializeComponent();
}
}
MainWindowViewModel.cs
public class MainWindowViewModel : BaseViewModel
{
private Page currentPage;
public Page CurrentPage { get { return currentPage; } set { currentPage = value; OnPropertyChanged("CurrentPage"); } }
private readonly ApplicationDbContext db;
public MainWindowViewModel(ApplicationDbContext db)
{
this.db = db;
CurrentPage = new PageConsignments();
}
#region Navigation Menu Commands
...
#endregion
}
And I can see that Db has been injected into MainWindow and the DataContext is correct (new instance of MainWindowViewModel).
However, when I navigate to ConsignmentsPage with CurrentPage = new PageConsignments I can see that the ConsingmentsPage.DataContext is null - presumably because this new view is not sitting in my Unity container.
I think I managed to resolve this by navigating with :
IUnityContainer container = new UnityContainer();
container.RegisterType<ApplicationDbContext>();
container.RegisterType<PageConsignmentsViewModel>();
var pc = container.Resolve<PageConsignments>();
CurrentPage = pc;
instead, but I was wondering - Is that the recommended method, or is there some way that I can define all these DIs in App.OnStartup rather than creating a new container for every navigation?

StructureMap | How to create a singleton instance / get always the same instance

I have a class UserManagement and always want to get the same instance (like singleton pattern). So my problem is now, that I always get a new instance insted of the named "Singleton". I'm new to StructureMap. I have tried both version (http://structuremap.github.io/object-lifecycle/) (http://structuremap.github.io/glossary/) described on the website.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Container container = new Container(new UserManagement());
//UserManagement instance = container.GetInstance<UserManagement>("Singleton");
UserManagement instance = container.GetInstance<UserManagement>();
MessageBox.Show(instance.Test);
}
}
public interface IUser
{
void DoSomethingFancy();
}
public class User : IUser
{
public void DoSomethingFancy()
{
}
}
public interface IDatabase
{
void DoSomething();
}
public class Database : IDatabase
{
public void DoSomething()
{
}
}
public class UserManagement : Registry
{
public string Test;
private Container _Container;
private IUser _User;
private IDatabase _Database;
public UserManagement()
{
_Container = new Container(_ =>
{
_.For<IUser>().Use<User>();
_.For<IDatabase>().Use<Database>();
});
_User = _Container.GetInstance<IUser>();
_Database = _Container.GetInstance<IDatabase>();
Test = DateTime.Now.ToString();
//For<UserManagement>().AddInstances(x =>
//{
// x.Type<UserManagement>().Named("Singleton");
//});
For<UserManagement>().Singleton();
}
}
Update
My solution has 3 projects
BootstrapperLibrary (class library - Bootstrapper)
Gui (WpfApp - MainWindow.xaml)
Framework (class library - LoginAction - UserManagement)
BootstrapperLibrary
Bootstrapper.cs
public static class Bootstrapper
{
public static Container Container => _Container ?? (_Container = InitializeContainer());
private static Container _Container;
public static Func<Container> InitializeContainer;
}
Gui
App.xaml.cs
public partial class App : Application
{
public App()
{
Bootstrapper.InitializeContainer += InitializeContainer;
}
private Container InitializeContainer()
{
Container container = new Container(c => c.Scan(scanner =>
{
scanner.TheCallingAssembly();
scanner.WithDefaultConventions();
scanner.AssembliesFromApplicationBaseDirectory();
}));
container.Configure(c =>
{
c.ForSingletonOf<UserManagement>();
c.For<IUser>().Use<UserAdv>();
}
);
return container;
}
}
MainWindow.xaml
<Window x:Class="Gui.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:myFramework="clr-namespace:MyFramework;assembly=MyFramework"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Button Content="Button" HorizontalAlignment="Left" Margin="383,228,0,0" VerticalAlignment="Top" Width="75">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<myFramework:LoginAction/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Grid>
</Window>
Framework
LoginAction.cs
public class LoginAction : TriggerAction<DependencyObject>
{
protected override void Invoke(object parameter)
{
UserManagement userManagement = Bootstrapper.Container.GetInstance<UserManagement>();
Console.WriteLine(userManagement.Test); //Just to see if it's still the same instance who holds for example the current user.
}
}
UserManagement.cs
public interface IUser
{
string Name { get; }
void DoSomethingFancy();
}
public class User : IUser
{
public string Name { get; } = "default";
public void DoSomethingFancy()
{
}
}
public class UserAdv : IUser
{
public string Name { get; } = "Advanced";
public void DoSomethingFancy()
{
}
}
public interface IDatabase
{
void DoSomething();
}
public class Database : IDatabase
{
public void DoSomething()
{
}
}
public class UserManagement
{
public string Test;
public IUser User;
private IDatabase _Database;
public UserManagement(IUser user, IDatabase database)
{
User = user;
_Database = database;
Test = DateTime.Now.ToString();
}
}
If I understood correctly, my main application now initializes and configures the Container within the application startup. The Bootstrapper class is holding a static instance there. MyFramework fetches an instance of UserManangement from the static Container instance.
Is this the correct way how to use StructureMap? Thank you for your patience!
You should declare:
ForConcreteType<UserManagement>().Singleton();
Edit:
You may implement a bootstrapper class and call the Initialize() method during program startup:
public sealed class Bootstrapper
{
private static StructureMap.Container _container;
public StructureMap.Container MyContainer
{
get { return _container; }
}
static Bootstrapper()
{
}
public static Initialize()
{
StructureMap.Configuration.DSL.Registry registry = new StructureMap.Configuration.DSL.Registry();
registry.For<IUser>().Use<User>();
registry.For<IDatabase>().Use<Database>();
registry.ForConcreteType<UserManagement>().Singleton();
_container = new Container(registry);
}
}
Remove the container initialization from the UserManagement class and let the DI framework inject the concrete types/objects.
public class UserManagement
{
public string Test;
private IUser _user;
private IDatabase _database;
public UserManagement(IUser user, IDatabase database)
{
_user = user;
_database = database;
Test = DateTime.Now.ToString();
}
}
Modify your WPF test window:
public partial class MainWindow : Window
{
static MainWindow()
{
// only for demonstration!!!
Bootstrapper.Initialize();
}
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Container container = Bootstrapper.MyContainer;
UserManagement instance = container.GetInstance<UserManagement>();
MessageBox.Show(instance.Test);
}
}

How to resolve view not closing on .Close()?

I've added a DialogService in order to open a ProductView, so far the ShowDetailDialog() is working as expected.
Issue:
I call Close() on the ProductView, the view isn't closed. I debugged this issue by setting a break point on the call to the dialog service close method.
When I stepped through the code, the null check shows that productView is null, which prevents Close() from being called.
Does anyone have idea why productView is null? (although it's showing data on the view)
DialogService:(hosts the Show and Close methods)
namespace MongoDBApp.Services
{
class DialogService : IDialogService
{
Window productView = null;
ProductView _productView;
public DialogService()
{
_productView = new ProductView();
}
public void CloseDetailDialog()
{
if (productView != null)
productView.Close();
}
public void ShowDetailDialog()
{
_productView.ShowDialog();
}
}
}
ProductViewModel: (summary of ProductVM, calls the close method on SaveCommand)
private void SaveProduct(object product)
{
_dialogService.CloseDetailDialog();
Messenger.Default.Send<ProductModel>(SelectedProduct);
}
CustomerOrdersViewmodel: (Where the ShowDetailDialog() is called initially)
private void EditOrder(object obj)
{
Messenger.Default.Send<ProductModel>(SelectedProduct);
_dialogService.ShowDetailDialog();
}
This is how I have always closed my windows.
Here would be my command:
class CancelCommand : ICommand
{
private NewTruckViewModel newTruck;
public CancelCommand(NewTruckViewModel vm)
{
newTruck = vm;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
newTruck.Cancel();
}
}
Here is my view Model and the method that gets called from my command:
private NewTruck myWnd; //View Declaration
//Ctor where I set myView (myWnd) equal to a view that is passed in.
public NewTruckViewModel(ObservableCollection<Truck> Trucks, NewTruck wnd, bool inEditTruck)
{
myEngine.stopHeartBeatTimer();
editTruck = inEditTruck;
myWnd = wnd;
SaveTruckCommand = new SaveTruckCommand(this);
CancelCommand = new CancelCommand(this);
ClearCommand = new ClearCommand(this);
SetLevel1MTCommand = new SetLevel1MTCommand(this);
SetLevel2MTCommand = new SetLevel2MTCommand(this);
SetLevel3MTCommand = new SetLevel3MTCommand(this);
SetLevel1FLCommand = new SetLevel1FLCommand(this);
SetLevel2FLCommand = new SetLevel2FLCommand(this);
SetLevel3FLCommand = new SetLevel3FLCommand(this);
myTrucks = Trucks;
}
public void Cancel()
{
myWnd.Close();
}
This works for me.
I resolved the issue by implementing an IDialogService on the View. Then calling the Show() and Close() methods from the ViewModel.
Solution:
Interface:
public interface IDialogService
{
void CloseDialog();
void ShowDialog(EditProductViewModel prodVM);
}
View:
public partial class ProductView : Window, IDialogService
{
public ProductView()
{
InitializeComponent();
this.DataContext = new EditProductViewModel(this);
}
public void CloseDialog()
{
if (this != null)
this.Visibility = Visibility.Collapsed;
}
public void ShowDialog(EditProductViewModel prodVM)
{
this.DataContext = prodVM;
this.Show();
}
private void Window_Closed(object sender, EventArgs e)
{
this.Visibility = Visibility.Collapsed;
}
}
ViewModel #1:
private IDialogService _dialogService;
public CustomerOrdersViewModel(IDialogService dialogservice)
{
this._dialogService = dialogservice;
}
private void EditOrder(object obj)
{
EditProductViewModel pvm = new EditProductViewModel(_dialogService);
pvm.Present(pvm);
Messenger.Default.Send<ProductModel>(SelectedProduct);
}
ViewModel #2:
private IDialogService _dialogService;
public EditProductViewModel(IDialogService dialogService)
{
this._dialogService = dialogService;
}
private void SaveProduct(object product)
{
SelectedProduct = SelectedProductTemp;
_dialogService.CloseDialog();
}
public void Present(EditProductViewModel prodVM)
{
_dialogService.ShowDialog(prodVM);
}

Wpf Caliburn.Micro view not loading

I am new to Wpf and Caliburn and I have the following scenario that i desire some help with, I am sure that the code is correct but this is not working as expected, to elaborate
My AppBootstrapper is thus:
public class AppBootstrapper : BootstrapperBase
{
private CompositionContainer _container;
public AppBootstrapper()
{
this.Initialize();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
this.DisplayRootViewFor<ShellViewModel>();
}
protected override void Configure()
{
try
{
this._container = new CompositionContainer(new AggregateCatalog(new DirectoryCatalog(".", "*")));
var batch = new CompositionBatch();
batch.AddExportedValue<IWindowManager>(new WindowManager());
batch.AddExportedValue<IEventAggregator>(new EventAggregator());
batch.AddExportedValue(this._container);
this._container.Compose(batch);
}
catch (Exception ex)
{
this._log.Error(ex.ToString());
}
}
protected override object GetInstance(Type service, string key)
{
try
{
var contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(service) : key;
var exports = this._container.GetExportedValues<object>(contract);
if (exports.Any())
{
return exports.First();
}
throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
}
catch (Exception ex)
{
if (ex is ReflectionTypeLoadException)
{
var typeLoadException = ex as ReflectionTypeLoadException;
var loaderExceptions = typeLoadException.LoaderExceptions;
if (loaderExceptions != null)
{
this._log.Error(loaderExceptions.First().ToString());
}
}
throw;
}
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return this._container.GetExportedValues<object>(AttributedModelServices.GetContractName(service));
}
protected override void BuildUp(object instance)
{
this._container.SatisfyImportsOnce(instance);
}
}
My ShellViewModel is as follows (i have removed some of the code to keep it short)
[Export(typeof(ShellViewModel))]
public class ShellViewModel : Conductor<IScreen>,
IHandle<NavigationEvent>,
IHandle<WindowCloseEnabledEvent>,
IShell,
IShellViewModel
{
private readonly IEventAggregator _eventAggregator;
[ImportingConstructor]
public ShellViewModel(IEventAggregator eventAggregator)
{
this._eventAggregator = eventAggregator;
}
[Import(typeof(LoginViewModel))]
public Lazy<IScreen> LoginViewModel { get; set; }
public void Loaded()
{
this._eventAggregator.Subscribe(this);
this.ActivateItem(this.LoginViewModel.Value);
}
}
I have an event trigger in the ShellView.xaml that triggers the Loaded method above.
I also have a LoginViewModel and LoginView (note that these are in ViewModels and Views folders in the project.
The important bit of LoginViewModel is
[Export(typeof(LoginViewModel))]
public class LoginViewModel : Screen, IAnimatableViewModel, ILoginViewModel
{
protected override void OnViewLoaded(object view)
{
this._view = view;
IoC.Get<ShellViewModel>().DisableClose = true;
base.OnViewLoaded(view);
}
}
And the LoginView.xaml at the moment simply displays some text.
I am using MEF as the DI container, the issue is that when I run up the application the ShellView is loaded which should load the LoginView into it but the LoginView does not load (or doesn't display)
If anyone can help it would be greatly appreciated.

Categories

Resources