目录
我喜欢用于构建应用程序的IHostBuilder/IHost方法。但是我发现配置必要的IHostBuilder令人困惑。还有一些功能,例如配置值的日志记录和加密/解密,我经常使用这些功能,我希望它们可以轻松地包含在IHost中。J4JHostConfiguration库是我完成所有这些的尝试。它还支持创建依赖项注入解析程序对象,你可以在缺少内置DI解析程序的任何C#应用程序中使用。
约束
有人曾经将代码框架描述为自愿穿的紧身衣。我在这里描述的扩展IHost和ViewModelLocator API需要使用 Autofac 作为其依赖注入组件。它还使用了我编写的其他几个库,例如J4JLogging,它以各种方式扩展了Serilog。我在这里使用的所有库都是开源的。
如果你有兴趣将这些系统适应其他库,我鼓励你fork GitHub存储库并拥有它。让我知道你做了什么!
介绍
创建IHost实例的基本方法(可用作大多数类型的Windows应用(也可能用作其他应用)的框架)涉及创建HostBuilder 的实例,对其进行配置,然后生成它。如果一切顺利,您将获得一个IHost实例,该实例为您提供了一个可用于即时检索服务的Services属性。
我发现配置HostBuilder相当令人困惑。对于我一直使用的某些功能尤其如此,例如日志记录,数据保护(即加密/解密)和配置命令行参数的解析方式。你可以做你需要的一切...但它不是特别直观。在使用IHost实例时,我还想参考一些属性,这些属性不容易访问或不可用,因为它们与我编写的其他支持库相关。
我扩展了IHost接口和IHostBuilder系统以满足这些额外的需求。派生接口为IJ4JHost:
public interface IJ4JHost : IHost
{
string Publisher { get; }
string ApplicationName { get; }
string UserConfigurationFolder { get; }
List<string> UserConfigurationFiles { get; }
string ApplicationConfigurationFolder { get; }
List<string> ApplicationConfigurationFiles { get; }
bool FileSystemIsCaseSensitive { get; }
StringComparison CommandLineTextComparison { get; }
ILexicalElements? CommandLineLexicalElements { get; }
CommandLineSource? CommandLineSource { get; }
OptionCollection? Options { get; }
OperatingSystem OperatingSystem { get; }
AppEnvironment AppEnvironment { get; }
}
可以通过在配置方法后的J4JHostConfiguration实例上调用该Build()方法来创建IJ4JHost实例。
您可以通过调用各种扩展方法来配置J4JHostConfiguration的实例。必须调用两个必需的方法:Publisher()用于定义应用发布者的名称,以及ApplicationName()用于定义应用程序的名称。这两个值对于解析配置文件路径和对加密/解密的内置支持都很重要。
您可以在 GitHub文档中阅读所有可选的扩展方法。
集中式依赖关系注入解析器的案例
就其本身而言,IJ4JHost是有用的。但我发现将其耦合到集中式依赖注入解析器更有用。原因如下。
虽然IJ4JHost系统允许您创建具有各种有用功能(例如,日志记录、命令行处理)的基于IHost的应用程序控制器,但它在简单的控制台应用中比在Windows桌面应用中更有用。原因与没有内置依赖项注入支持的后果有关。
在简单的控制台应用中,通常一次只运行一个“事物”。合并依赖注入相对简单,因为通常,动态创建对象的唯一特殊情况是获取第一个单例应用程序控制器。完成此操作后,大多数控制台应用体系结构只需根据需要创建对象,根据创建对象的代码的本地信息创建对象。或者它似乎在我的简单控制台应用程序中;您的里程可能会有所不同。
Windows桌面应用程序完全不同。如果可能的话,很难创建一个根应用程序控制器并按需在本地创建所有内容。它与激活代码以及代码所需的对象的多种方式有关。
类似的事情也发生在AspNetCore应用程序中。但AspNetCore内置了对依赖注入的支持。您可以使用必须动态创建的构造函数参数来定义对象,并且,如果您已向依赖项注入框架注册了参数类型,则可以确保一切正常。
我不知道有任何Windows桌面体系结构包含对依赖注入的相同类型的内置支持。Windows Forms没有它。WPF 没有它。Windows App v2 没有它。Windows App v3没有它,尽管我认为它在要添加的路线图上。UWP 可能拥有它;我从未在 UWP 中做过任何工作。
最终结果是,要在大多数或所有Windows桌面体系结构中使用依赖注入,您需要使用某种ViewModelLocator模式:一个带有static方法的类,可以调用这些方法来创建按需向依赖关系注入系统注册的对象。
我编写J4JDeusEx是为了有一个通用的ViewModelLocator对象,它与我的IJ4JHost API集成在一起,这样无论我是在编写控制台应用程序还是Windows桌面应用程序,我都可以以统一的方式与依赖注入进行交互。它的接口全部是static,而且非常简单:
public class J4JDeusEx
{
public static IServiceProvider ServiceProvider { get; protected set; }
public static bool IsInitialized { get; protected set; }
public static string? CrashFilePath { get; protected set; }
public static IJ4JLogger? Logger { get; protected set; }
public static void OutputFatalMessage( string msg, IJ4JLogger? logger );
}
您可以在GitHub文档中阅读有关J4JDeusEx的体系结构和功能的更多信息。
使用代码
如果您不想使用J4JDeusEx,构建IJ4JHost的实例非常简单:
var hostConfig = new J4JHostConfiguraton();
// configuration steps omitted; check the GitHub documentation for details
var host = hostConfig.Build();
但是,建议在调用Build()之前检查以确保J4JHostConfiguration对象已正确配置:
var hostConfig = new J4JHostConfiguraton();
// configuration steps omitted; check the GitHub documentation for details
IJ4JHost? host = null;
if( hostConfig.MissingRequirements == J4JHostRequirements.AllMet )
host = hostConfig.Build();
else
{
// take remedial action, abort startup, etc.
}
如果要利用J4JDeusEx的功能,该过程会稍微复杂一些。创建一个J4JDeusExHosted (用于非沙盒环境)或J4JDeusExWinApp (用于沙盒环境)的实例,并实现一个abstract protected方法:
protected override J4JHostConfiguration? GetHostConfiguration();
下面是典型的派生类的外观:
// This class is marked as partial, but that's simply to keep the codebase clean
// check the GitHub documentation for details
internal partial class DeusEx : J4JDeusExHosted
{
protected override J4JHostConfiguration? GetHostConfiguration()
{
var hostConfig = new J4JHostConfiguration( AppEnvironment.Console )
.ApplicationName( "WpFormsSurveyProcessor" )
.Publisher( "Jump for Joy Software" )
.LoggerInitializer( ConfigureLogging )
.AddDependencyInjectionInitializers
( ConfigureDependencyInjection )
.FilePathTrimmer( FilePathTrimmer );
var cmdLineConfig = hostConfig.AddCommandLineProcessing
( CommandLineOperatingSystems.Windows )
.OptionsInitializer( SetCommandLineConfiguration )
.ConfigurationFileKeys
( true, false, "c", "config" );
return hostConfig;
}
// remaining details omitted for clarity
}
最后一步是调用派生类的Initialize()方法。在何处执行此操作取决于你编写的是控制台应用还是Windows桌面应用。
控制台应用
internal class Program
{
static void Main( string[] args )
{
var deusEx = new DeusEx();
if( !deusEx.Initialize() )
{
J4JDeusEx.Logger?.Fatal("Could not initialize application");
Environment.ExitCode = 1;
return;
}
// launch application, usually via calling a service
}
// remaining details omitted for clarity
}
Window桌面应用
public partial class App : Application
{
private readonly IJ4JLogger _logger;
public App()
{
this.InitializeComponent();
this.UnhandledException += App_UnhandledException;
var deusEx = new GPSLocatorDeusEx();
if ( !deusEx.Initialize() )
throw new J4JDeusExException( "Couldn't configure J4JDeusEx object" );
_logger = J4JDeusEx.ServiceProvider.GetRequiredService<IJ4JLogger>();
}
private void App_UnhandledException
( object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e )
{
J4JDeusEx.OutputFatalMessage
($"Unhandled exception: {e.GetType().Name}", null);
J4JDeusEx.OutputFatalMessage( $"{e.Message}", null );
}
}
Windows桌面示例还显示了如何使用J4JDeusEx的崩溃文件组件。我们为未处理的异常实现自定义处理程序,并使用J4JDeusEx.OutputFatalMessage()将异常信息写入崩溃文件。
J4JDeusEx初始化后,你可以在代码库的任何地方使用它作为ViewModelLocator:
var service = J4JDeusEx.ServiceProvider.Services.GetRequiredService<IFooBar>();
沙盒环境与非沙盒环境
沙盒环境是指应用无法不受限制地访问文件系统。在非沙盒环境中,应用可以访问文件系统的任何部分。
Windows窗体、WPF和控制台应用是非沙盒环境的示例。
Windows Applications v3和UWP(以及WinRT)是沙盒环境的示例。
兴趣点
IJ4JHost和J4JDeusEx是从我以前编写的单个库演变而来的。像许多代码编写一样,它们的开发是意识到我一遍又一遍地使用相同或相似的模式并且可以将它们抽象到一个通用框架中的结果。这就是为什么他们每个人都不时地:)经历一些非常根本的重组。
https://www.codeproject.com/Articles/5350789/Enhanced-Csharp-IHostBuilder-IHost-and-Dependency