英文:
Object reference exeption - Dependency Injection with Entity Framework WPF
问题
以下是您提供的代码部分的翻译:
我尝试使用Entity Framework和依赖注入来实现与数据库的连接。<br> 我想在App.xaml.cs中创建Host。
public partial class App : Application
{
public static IHost? AppHost { get; private set; }
public App()
{
AppHost = Host.CreateDefaultBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddSingleton<LoginWindow>();
services.AddSingleton<LoginViewModel>();
services.AddDbContext<KnitterNotebookContext>(
options =>
{
string appSettingsPath = Path.Combine(ProjectDirectory.ProjectDirectoryFullPath, "appsettings.json");
string appSettingsString = File.ReadAllText(appSettingsPath);
AppSettings AppSettings = JsonConvert.DeserializeObject<AppSettings>(appSettingsString)!;
options.UseSqlServer(AppSettings.KnitterNotebookConnectionString);
});
})
.Build();
}
protected override async void OnStartup(StartupEventArgs e)
{
await AppHost!.StartAsync();
var startupWindow = AppHost.Services.GetRequiredService<LoginWindow>();
startupWindow.Show();
base.OnStartup(e);
}
protected override async void OnExit(ExitEventArgs e)
{
await AppHost!.StopAsync();
base.OnExit(e);
}
}
我想将DbContext作为参数传递给ViewModel,但这样做时会引发异常。
public class LoginViewModel : BaseViewModel
{
public LoginViewModel(KnitterNotebookContext knitterNotebookContext)
//public LoginViewModel()
{
KnitterNotebookContext = knitterNotebookContext;
ShowRegistrationWindowCommand = new RelayCommand(ShowRegisterWindow);
LogInCommandAsync = new AsyncRelayCommand(LogIn);
}
private KnitterNotebookContext KnitterNotebookContext { get; set; }
}
在代码中提到的问题是,您想将DbContext作为参数传递给LoginViewModel,但出现了异常。如果使用无参数构造函数的LoginViewModel并使用new()
创建新的KnitterNotebookContext实例,就不会出现问题。您想知道如何解决这个问题。
英文:
I tried to implement connection to database using Entity Framework and Dependency Injection. <br> I want to create Host in App.xaml.cs.
public partial class App : Application
{
public static IHost? AppHost { get; private set; }
public App()
{
AppHost = Host.CreateDefaultBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddSingleton<LoginWindow>();
services.AddSingleton<LoginViewModel>();
services.AddDbContext<KnitterNotebookContext>(
options =>
{
string appSettingsPath = Path.Combine(ProjectDirectory.ProjectDirectoryFullPath, "appsettings.json");
string appSettingsString = File.ReadAllText(appSettingsPath);
AppSettings AppSettings = JsonConvert.DeserializeObject<AppSettings>(appSettingsString)!;
options.UseSqlServer(AppSettings.KnitterNotebookConnectionString);
});
})
.Build();
}
protected override async void OnStartup(StartupEventArgs e)
{
await AppHost!.StartAsync();
var startupWindow = AppHost.Services.GetRequiredService<LoginWindow>();
startupWindow.Show();
base.OnStartup(e);
}
protected override async void OnExit(ExitEventArgs e)
{
await AppHost!.StopAsync();
base.OnExit(e);
}
I want to pass DbContext as parameter to ViewModel, but when I do, it throws exception.
public class LoginViewModel : BaseViewModel
{
public LoginViewModel(KnitterNotebookContext knitterNotebookContext)
//public LoginViewModel()
{
KnitterNotebookContext = knitterNotebookContext;
ShowRegistrationWindowCommand = new RelayCommand(ShowRegisterWindow);
LogInCommandAsync = new AsyncRelayCommand(LogIn);
}
private KnitterNotebookContext KnitterNotebookContext { get; set; }
}
There is no problem if I use parameterless constructor of LoginViewModel and create new instances of KnitterNotebookContext with new(), but I want to pass it as a parameter. How to solve it?
答案1
得分: 1
我碰巧在某个时候遇到了相同的问题 - 我猜想你正在XAML中为MainWindow
设置DataContext
,就像这样:
<Window.DataContext>
<local:LoginViewModel/>
</Window.DataContext>
以这种方式执行时,当创建LoginWindow
时,会尝试创建其DataContext
的新实例(即LoginViewModel
),但它不知道如何解析构造函数 - 因为您通过服务进行了注入,它不再是无参数的。
您必须提供一种方法,以强制DataContext
由ServiceProvider
(所有已注入服务的容器)实例化。
您可以通过使用可在XAML中使用的帮助类(继承自MarkupExtension
)来实现这一点,就像下面的示例一样:
public class ServiceDispatcher : MarkupExtension
{
public static Func<Type, object> Resolver { get; set; }
public Type Type { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return Resolver?.Invoke(Type);
}
}
并在App.xaml.cs
中分配其服务分发Resolver
委托:
public App()
{
AppHost = Host.CreateDefaultBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddSingleton<LoginWindow>();
services.AddSingleton<LoginViewModel>();
services.AddDbContext<KnitterNotebookContext>(
options =>
{
string appSettingsPath = Path.Combine(ProjectDirectory.ProjectDirectoryFullPath, "appsettings.json");
string appSettingsString = File.ReadAllText(appSettingsPath);
AppSettings AppSettings = JsonConvert.DeserializeObject<AppSettings>(appSettingsString)!;
options.UseSqlServer(AppSettings.KnitterNotebookConnectionString);
});
})
.Build();
ConfigureServiceProvider(AppHost.Services);
}
private static void ConfigureServiceProvider(IServiceProvider appHost)
{
ServiceDispatcher.Resolver = (type) =>
{
return appHost.GetRequiredService(type);
};
}
现在,您可以使用您的ServiceDispatcher
以以下语法动态提供DataContext
实例给您的Views
:
<Window.DataContext>
<base:ServiceDispatcher Type="{x:Type local:LoginViewModel}"/>
</Window.DataContext>
英文:
I happened to encounter the same problem at one time - i'm guessing that you are setting DataContext
for MainWindow
in xaml, like this:
<Window.DataContext>
<local:LoginViewModel/>
<Window.DataContext>
Doing it this way, when your LoginWindow
is created, tries to create new instance of its DataContext
(your LoginViewModel
), but it does not know how to resolve constructor - since you injected it with service, it's not parameterless anymore.
You have to provide a way, to enforce DataContext
to be instantiated by ServiceProvider
(a container for all injected services).
You could achieve this through use of a helper class, which is usable in xaml (inherits from MarkupExtension
), like this one below.
public class ServiceDispatcher : MarkupExtension
{
public static Func<Type, object> Resolver { get; set; }
public Type Type { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return Resolver?.Invoke(Type);
}
}
And assigning it's service dispatching Resolver
delegate in your App.xaml.cs
public App()
{
AppHost = Host.CreateDefaultBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddSingleton<LoginWindow>();
services.AddSingleton<LoginViewModel>();
services.AddDbContext<KnitterNotebookContext>(
options =>
{
string appSettingsPath = Path.Combine(ProjectDirectory.ProjectDirectoryFullPath, "appsettings.json");
string appSettingsString = File.ReadAllText(appSettingsPath);
AppSettings AppSettings = JsonConvert.DeserializeObject<AppSettings>(appSettingsString)!;
options.UseSqlServer(AppSettings.KnitterNotebookConnectionString);
});
})
.Build();
ConfigureServiceProvider(AppHost.Services);
}
private static void ConfigureServiceProvider(IServiceProvider appHost)
{
ServiceDispatcher.Resolver = (type) =>
{
return appHost.GetRequiredService(type);
};
}
Now you can use your ServiceDispatcher
to dynamically provide DataContext
instance for your Views
using syntax below.
<Window.DataContext>
<base:ServiceDispatcher Type="{x:Type local:LoginViewModel}"/>
</Window.DataContext>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论