温莎城堡 XML 配置帮助

发布于 2024-10-03 15:49:50 字数 1520 浏览 1 评论 0原文

我在我的应用程序的 Caste-Windsor XML 配置中定义了以下三个组件:

<component id="StringFactory"
           service="IStringFactory, MyApp"
           type="DefaultStringFactory, MyApp"
           lifestyle="singleton"
           />

<component id="TheString"
           type="System.String"
           factoryId="StringFactory"
           factoryCreate="CreateString"
           >
  <parameters>
    <name>SomeString</name>
  </parameters>
</component>

<component id="TheTarget"
           service="ITarget, MyApp"
           type="TheTarget, MyApp"
           lifestyle="transient"
           >
  <parameters>
    <aString>${TheString}</aString>
  </parameters>
</component>

并定义了以下功能:

<facility id="factory.support"
          type="Castle.Facilities.FactorySupport.FactorySupportFacility, Castle.MicroKernel"
          />

当我运行应用程序并在 TheObject 类的构造函数中设置断点时,作为 aString 参数传入的值是“${TheString}”,当我希望它解析为具有该名称的组件的值时。

另外,我在 StringFactory 构造函数和 CreateString 方法中有一个断点,但都没有命中。我知道该配置正在被使用,因为其他组件正在正确解析。

我在这里错过了什么或做错了什么?

更新

鉴于此主题所涉及的巨大切线,我重构了上面的代码以删除与连接字符串有关的任何内容。这篇文章的初衷是用另一个对象上的方法返回的值来注入属性。不知怎的,在关于为什么我使用 XML 与基于代码的配置以及这是否是注入连接字符串的好方法的讨论中,这一点被忽略了。

上述方法远非最初的想法,它是从有关该主题的其他几个讨论中提取出来的,我们的要求就是它们。我希望帮助理解为什么现有的配置(无论是否正确)无法按预期工作。

我确实验证了前两个组件是否已正确实例化。当我调用 Container.Resolve("TheString") 时,我得到了正确的值。无论出于何种原因,参数语法都无法正常工作。

有什么想法吗?

I have the following three components defined in the Caste-Windsor XML configuration for my application:

<component id="StringFactory"
           service="IStringFactory, MyApp"
           type="DefaultStringFactory, MyApp"
           lifestyle="singleton"
           />

<component id="TheString"
           type="System.String"
           factoryId="StringFactory"
           factoryCreate="CreateString"
           >
  <parameters>
    <name>SomeString</name>
  </parameters>
</component>

<component id="TheTarget"
           service="ITarget, MyApp"
           type="TheTarget, MyApp"
           lifestyle="transient"
           >
  <parameters>
    <aString>${TheString}</aString>
  </parameters>
</component>

And the following facility defined:

<facility id="factory.support"
          type="Castle.Facilities.FactorySupport.FactorySupportFacility, Castle.MicroKernel"
          />

When I run the application and set a breakpoint in the constructor of the TheObject class, the value passed in as the aString parameter is "${TheString}" when I expect it to resolve to the value of the component with that name.

Also, I have a breakpoint in the StringFactory constructor and CreateString method, neither of which are hit. I know the configuration is being used as other components are resolving correctly.

What am I missing or doing wrong here?

UPDATE

In light of the huge tangient this topic has taken, I've refactored the code above to remove anything to do with connection strings. The original intent of this post was about injecting a property with the value returned from a method on another object. Somehow that point was lost in a discussion about why I'm using XML versus code-based configuration and if this is a good way to inject a connection string.

The above approach is far from an original idea and it was pulled from several other discussions on this topic and our requirements are what they are. I'd like help understanding why the configuration as it is in place (whether the right approach or not) isn't working as expected.

I did verify that the first two components are being instantiated correctly. When I call Container.Resolve("TheString"), I get the correct value back. For whatever reason, The parameter syntax is not working correctly.

Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

尾戒 2024-10-10 15:49:50

虽然这不是我在应用程序中需要执行的操作的明确解决方案,但我相信我已经找出了代码的问题所在。或者至少我找到了一种让它发挥作用的方法,这暗示了最初的问题。

我用自定义类替换了 TheString 的 String 类型。就是这样。一旦我这样做了,一切就都正常了。

我的猜测是,这与我尝试使用 ValueType(基元)作为组件有关。我猜Castle不支持它。

因此,知道情况确实如此,我现在可以继续弄清楚这种方法是否真的有效,或者我们是否需要改变方向。

更新

为了完整起见,我想我应该继续解释我做了什么来解决我的问题并满足我的要求。

和以前一样,我可以通过定义为的 IConfigurationService 访问我的配置设置:

<component id="ConfigurationService"
           service="MyApp.IConfigurationService, MyApp"
           type="MyApp.RuntimeConfigurationService, MyApp"
           lifestyle="singleton"
           />

这会自动注入到我的(新)IConnectionFactory 中,IConnectionFactory 负责根据应用程序配置文件中定义的连接字符串生成 IDbConnection 对象。工厂声明为:

<component id="ConnectionFactory"
           service="MyApp.Factories.IConnectionFactory, MyApp"
           type="MyApp.Factories.DefaultConnectionFactory, MyApp"
           lifestyle="singleton"
           />

为了解决我的存储库使用的连接,我使用 ConnectionFactory 将每个连接声明为一个组件来创建每个实例:

<component id="MyDbConnection"
           type="System.Data.IDbConnection,
                 System.Data, Version=2.0.0.0, Culture=neutral,
                 PublicKeyToken=b77a5c561934e089"
           factoryId="ConnectionFactory"
           factoryCreate="CreateConnection"
           lifestyle="transient"
           >
  <parameters>
    <connectionStringName>MyDB</connectionStringName>
  </parameters>
</component>

请注意对 System.Data 的完整描述的引用。我发现每当在 GAC 中引用程序集时,这是必要的。

最后,我的存储库定义为:

<component id="MyRepository"
           service="MyApp.Repositories.IMyRepository, MyApp"
           type="MyApp.Sql.SqlMyRepository, MyApp.Sql"
           lifestyle="transient"
           >
  <parameters>
    <connection>${MyDbConnection}</connection>
  </parameters>
</component>

现在一切都正确解析,并且我没有将任何硬编码字符串编译到我的代码中。没有连接字符串名称、应用程序设置键或其他任何内容。该应用程序可以从 XML 文件完全重新配置,这是我必须满足的要求。另外,将使用该解决方案的其他开发人员可以按照他们习惯的方式管理实际的连接字符串。双赢。

希望这对遇到类似情况的其他人有所帮助。

While not a definitive solution to what I need to do in my application, I believe I've figured out what is wrong with the code. Or at least I've found a way to make it work which hints at the original problem.

I replaced the String type for TheString with a custom class. That's it. Once I did that, everything worked fine.

My guess is that it has something to do with the fact that I was trying to use a ValueType (primitive) as a component. I guess Castle doesn't support it.

So, knowing that's the case, I can now move on to figuring out if this approach is really going to work or if we need to change direction.

UPDATE

For the sake of completeness, I thought I'd go ahead and explain what I did to solve my problem AND satisfy my requirements.

As before, I have access to my configuration settings through an IConfigurationService defined as:

<component id="ConfigurationService"
           service="MyApp.IConfigurationService, MyApp"
           type="MyApp.RuntimeConfigurationService, MyApp"
           lifestyle="singleton"
           />

This is automatically injected into my (new) IConnectionFactory which is responsible for generating IDbConnection objects based on the connection strings defined in the application's configuration file. The factory is declared as:

<component id="ConnectionFactory"
           service="MyApp.Factories.IConnectionFactory, MyApp"
           type="MyApp.Factories.DefaultConnectionFactory, MyApp"
           lifestyle="singleton"
           />

In order to resolve what connection is used by my repository, I declare each connection as a component using the ConnectionFactory to create each instance:

<component id="MyDbConnection"
           type="System.Data.IDbConnection,
                 System.Data, Version=2.0.0.0, Culture=neutral,
                 PublicKeyToken=b77a5c561934e089"
           factoryId="ConnectionFactory"
           factoryCreate="CreateConnection"
           lifestyle="transient"
           >
  <parameters>
    <connectionStringName>MyDB</connectionStringName>
  </parameters>
</component>

Notice the fully described reference to System.Data. I found this is necessary whenever referencing assemblies in the GAC.

Finally, my repository is defined as:

<component id="MyRepository"
           service="MyApp.Repositories.IMyRepository, MyApp"
           type="MyApp.Sql.SqlMyRepository, MyApp.Sql"
           lifestyle="transient"
           >
  <parameters>
    <connection>${MyDbConnection}</connection>
  </parameters>
</component>

Now everything resolves correctly and I don't have ANY hard-coded strings compiled into my code. No connection string names, app setting keys or whatever. The app is completely reconfigurable from the XML files which is a requirement I must satisfy. Plus, other devs that will be working with the solution can manage the actual connection strings in the way they are used to. Win-win.

Hope this helps anyone else that runs into a similar scenario.

倾城月光淡如水﹏ 2024-10-10 15:49:50

这里实际上并不需要 XML 注册,因为您可能不需要在不重新编译的情况下交换组件或更改所使用的方法。编写可配置应用程序并不意味着必须使用 XML 注册。

您发布的这个特定 XML 注册的问题在于,连接字符串是一个参数,但它被视为服务。

通过代码注册来完成此操作要容易得多,例如:

var container = new WindsorContainer();
container.Register(Component.For<IConfigurationService>().ImplementedBy<RuntimeConfigurationService>());
container.Register(Component.For<ITheRepository>().ImplementedBy<TheRepository>()
                            .LifeStyle.Transient
                            .DynamicParameters((k, d) => {
                                var cfg = k.Resolve<IConfigurationService>();
                                d["connectionString"] = cfg.GetConnectionString();
                                k.ReleaseComponent(cfg);
                            }));

或者如果您不想依赖 IConfigurationService,您可以执行以下操作:

container.Register(Component.For<ITheRepository>().ImplementedBy<TheRepository>()
                            .LifeStyle.Transient
                            .DependsOn(Property.ForKey("connectionString")                                             
                                               .Is(ConfigurationManager.ConnectionStrings[ConfigurationManager.AppSettings["connName"]].ConnectionString))

You don't really need XML registrations here, since you probably don't need to swap components or change the method used without recompiling. Writing a configurable app does not imply having to use XML registrations.

The problem with this particular XML registration you posted is that the connection string is a parameter, but it's treated like a service.

Doing this with code registrations is much easier, e.g.:

var container = new WindsorContainer();
container.Register(Component.For<IConfigurationService>().ImplementedBy<RuntimeConfigurationService>());
container.Register(Component.For<ITheRepository>().ImplementedBy<TheRepository>()
                            .LifeStyle.Transient
                            .DynamicParameters((k, d) => {
                                var cfg = k.Resolve<IConfigurationService>();
                                d["connectionString"] = cfg.GetConnectionString();
                                k.ReleaseComponent(cfg);
                            }));

Or if you don't want to depend on IConfigurationService, you could do something like:

container.Register(Component.For<ITheRepository>().ImplementedBy<TheRepository>()
                            .LifeStyle.Transient
                            .DependsOn(Property.ForKey("connectionString")                                             
                                               .Is(ConfigurationManager.ConnectionStrings[ConfigurationManager.AppSettings["connName"]].ConnectionString))
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文