将Castle Windsor与配置数据相结合

时间:2015-01-09 08:51:38

标签: c# castle-windsor

在我的项目中,我有一些对象用作多个服务的一部分。以下是架构的简要示例(故意遗漏的接口):

public class Plc : IPlc
{
    public Plc(string connectionString) { ... }
}

public class PlcRegister
{
    public string Address { get; set; }
    public int Length { get; set; }
}

public class PlcJobWriter : IPlcJobWriter
{
    public PlcJobWriter(IPlc plc,
        PlcRegister commandRegister,
        PlcRegister statusRegister,
        ...)
    { ... }
}

public class JobService : IJobService
{
    public JobService(Dictionary<string, PlcJobWriter> plcJobWriters) { ... }
}

public class PlcProgramWriter : IPlcProgramWriter
{
    public PlcProgramWriter(IPlc plc,
        PlcRegister commandRegister,
        PlcRegister statusRegister,
        ...)
    { ... }
}

public class ProgramService : IProgramService
{
    public ProgramService(List<PlcProgramWriter> plcProgramWriters) { ... }
}

我将在生产中获得的是一个或多个PLC。每个Plc都会有一个PlcProgramWriter和一个或多个PlcJobWriter。任何程序更新都将写入所有PlcProgramWriters(因此plcProgramWriters中的ProgramService参数,并且作业将写入匹配的PlcJobWriter,与plcJobWriters中的密钥匹配在JobService构造函数中。

我希望Plc connectionString所需的值以及PlcJobWriterPlcProgramWriter可通过app.config配置的commandRegister和statusRegister,以及{{1}的密钥用于查找匹配的JobWriter。

我可以定义自己的配置架构并编写代码来处理配置数据,但是如果我想让Windsor处理所有这些,我会更加感兴趣。我不介意添加基础设施来实现这一目标,但我根本找不到任何这样的解决方案的例子(可能是在寻找错误的东西)。

2 个答案:

答案 0 :(得分:1)

这并没有真正回答问题,但如果通过获取对web.config值的类型化访问来满足您的一个要求,那么您有两个选项。

一,您可以创建一个派生自ConfigurationSection的类,这需要您在web.config <configSections>中注册该类型,呃,部分,如此:

<section name="sectionName" type="Fully.Qualified.Namespace.CustomSettings, YourAssemblyName" />

然后该部分本身看起来像:

<customSettings propertyName1="somevalue" />

它的优点是能够通过数据注释强制执行某些规则。这是我一度用于Akismet助手的实现:

public class AkismetSettings : ConfigurationSection
{
    private static AkismetSettings settings = ConfigurationManager.GetSection("akismet") as AkismetSettings;

    public static AkismetSettings Settings
    {
        get
        {
            return settings;
        }
    }

    [ConfigurationProperty("key", IsRequired = true)]
    public string Key
    {
        get { return (string)this["key"]; }
        set { this["key"] = value; }
    }


    [ConfigurationProperty("registeredsite", IsRequired = true)]
    public string RegisteredSite
    {
        get { return (string)this["registeredsite"]; }
        set { this["registeredsite"] = value; }
    }
}

及其相关部分:

<akismet key="0000000000" registeredsite="http://example.com/" />

我会这样使用它:

var helper = new AkismetHelper(AkismetSettings.Settings.Key, AkismetSettings.Settings.RegisteredSite);

我没有对帮助器的持续需求,并且通过Windsor解析实例似乎有些过分,所以当我需要它时,我只是新建了一个。

两个我更喜欢的选项是简单地创建一个从<appSettings>读取相关值的类,并进行必要的类型转换。

这是我目前正在使用的精简版。

public class AppSettings
{
    public static int MinPasswordLength
    {
        get
        {
            //  Recommended minimum password length.
            //  See https://www.owasp.org/index.php/Password_length_%26_complexity
            int min = 8;

            if (!string.IsNullOrWhiteSpace(ConfigurationManager.AppSettings["app.minPasswordLength"]))
            {
                min = int.Parse(ConfigurationManager.AppSettings["app.minPasswordLength"]);
            }

            return min;
        }
    }

    /* ... */

    public static string Find(string key)
    {
        return ConfigurationManager.AppSettings[key] ?? string.Empty;
    }
}

您可以使用相同的想法为<connectionStrings>部分编写更清晰的访问者。如果您想使用<appSettings>,但不希望在web.config中有很多值,则有助于记住<appSettings>具有可用于链接的file属性到包含.config部分的另一个<appSettings>文件:

<appSettings file="settings.config">

答案 1 :(得分:1)

我认为应该采用如下方法:

首先,您需要在web.config中的configSections中使用此功能。名称可以是任何名称:

<section name="castle" type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />

然后在web.config中创建包含DI配置的castle部分。我已经尝试根据您的代码和您的要求描述给出了我能在这里得到的最佳示例。重要的一点是,您使用${ }语法通过其ID引用其他组件,类型名称需要完全限定命名空间和程序集,并且如果您为每个接口定义了多个实现然后你可能需要通过键而不是接口类型来解决。

<castle>
    <properties>
        <connectionString>YourConnectionStringHere</connectionString>
    </properties>

    <components>
         <component id="PlcA" service="Namespace.IPlc, AssemblyName" type="Namespace.Plc, AssemblyName">
             <parameters>
                 <connectionString>${connectionString}</connectionString>
             </parameters>
         </component>
         <!-- Other IPlc implementations as required -->

         <component id="PlcJobWriterA" service="Namespace.IPlcJobWriter, AssemblyName" type="Namespace.PlcJobWriter, AssemblyName">
              <parameters>
                  <plc>${PlcA}</plc>
                  <commandRegister>
                      <parameters>
                          <address>CommandRegisterAddress</address>
                          <length>CommandRegisterLength</length>
                      <parameters>
                   </commandRegister>
                   <statusRegister>
                      <parameters>
                          <address>StatusRegisterAddress</address>
                          <length>StatusRegisterLength</length>
                      <parameters>
                   </statusRegister>
              </parameters>
         </component>
         <!-- Other PlcJobWriters as required -->
         <!-- PlcProgramWriter can be configured in the same way -->

         <component id="JobService" service="Namespace.IJobService, AssemblyName" type="Namespace.JobService, AssemblyName">
             <parameters>
                 <plcJobWriters>
                     <dictionary keyType="System.String" valueType="Namespace.IPlcJobWriter, AssemblyName">
                         <entry key="JobWriterA">${PlcJobWriterA}</entry>
                         <!-- Other entries as required -->
                     </dictionary>
                 </plcJobWriters>  
             </parameters>
         </component>

         <component id="ProgramService" service="Namespace.IProgramService, AssemblyName" type="Namespace.ProgramService, AssemblyName">
             <parameters>
                 <plcProgramWriters>
                     <list>
                         <item>${ProgramWriterA}</item>
                         <!-- Other items as required -->
                     </list>
                 </plcProgramWriters>
             </parameters>
         </component>
    </components>
</castle>

然后,您可以在创建时将配置部分的名称传递给Windsor容器:

using Castle.Core.Resource;
using Castle.Windsor;
using Castle.Windsor.Configuration.Interpreters;

...

using (var castleConfig = new ConfigResource("castle"))
{
     container = new WindsorContainer(new XmlInterpreter(castleConfig));
}   

显然,这不是一个完整的工作解决方案,但希望它应该说明如何使用Castle的XML配置。 Castle的错误信息往往对任何有问题的信息都很有用。更多信息可以在这里找到:http://docs.castleproject.org/Windsor.XML-Registration-Reference.ashx