在 autofac 中使用模块和配置文件

发布于 11-02 17:26 字数 3520 浏览 1 评论 0原文

虽然我通常认为 Autofac 文档(在 wiki 上)很有帮助,但关于 XML 配置和模块的部分对我来说有点不清楚。现在,我有一个示例工作(我在下面介绍),但我不确定它是否代表了 Autofac 上下文中的一种混蛋配置方法。特别是,我不确定配置文件和代码文件中是否有更多或更少的我真正需要的内容。

这是代码:

using System;
using System.IO;
using Autofac;
using Autofac.Configuration;

namespace AutofacTest.Animals
{
    interface IAnimal
    {
        void Speak ( );
    }

    abstract class Animal : IAnimal
    {
        protected TextWriter Writer
        {
            get;
            private set;
        }

        protected Animal ( TextWriter writer )
        {
            this.Writer = writer;
        }

        public abstract void Speak ( );

    }

    class Dog : Animal
    {

        public Dog ( TextWriter writer )
            : base ( writer )
        {

        }

        public override void Speak ( )
        {
            this.Writer.WriteLine ( "Arf!" );
        }
    }

    class Cat : Animal
    {
        public Cat ( TextWriter writer )
            : base ( writer )
        {

        }

        public override void Speak ( )
        {
            this.Writer.WriteLine ( "Meow" );
        }
    }

    // In actual practice, this would be in a separate assembly, right?
    class AnimalModule : Module
    {
        protected override void Load ( ContainerBuilder builder )
        {
            builder.RegisterInstance ( Console.Out ).As<TextWriter> ( ).SingleInstance ( );
            builder.Register ( d => new Dog ( d.Resolve<TextWriter> ( ) ) ).As<IAnimal> ( );
        }
    }

    class Program
    {
        static void Main ( )
        {
            Console.ForegroundColor = ConsoleColor.Yellow;

            ContainerBuilder builder = new ContainerBuilder ( );
            ConfigurationSettingsReader reader = new ConfigurationSettingsReader(); 
            builder.RegisterModule ( reader );
            //builder.RegisterModule ( new AnimalModule ( ) );
            builder.Build ( ).Resolve<IAnimal> ( ).Speak ( );
            Console.ReadKey ( );
        }
    }
}

这是一个关联的配置文件:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="autofac" type="Autofac.Configuration.SectionHandler, Autofac.Configuration"/>
    </configSections>
    <autofac defaultAssembly="AutofacTest">
        <components>
            <component
                type="AutofacTest.Animals.Cat"
                service="AutofacTest.Animals.IAnimal" />
            <component type="System.IO.StreamWriter" service="System.IO.TextWriter">
                <parameters>
                    <parameter name="path" value="C:\AutofacTest.txt"/>
                    <parameter name="append" value="false" />
                </parameters>
                <properties>
                    <property name="AutoFlush" value="true" />
                </properties>
            </component>
        </components>
        <modules>
            <module type="AutofacTest.Animals.AnimalModule, AutofacTest"/>
        </modules>
    </autofac>
</configuration>

这一切都工作正常。应用程序将“Meow”输出到文本文件。如果我注释掉组件元素,应用程序会输出“Arf!”到控制台。

那么,这里一切都好吗?或者有更好的方法来解决这个问题吗?

我对基于模块的配置背后的想法有点不确定:

在实际实践中,模块应该与应用程序的其余部分位于单独的程序集中,我是否正确?

我是否正确理解模块的主要功能之一是为 DI 容器提供默认配置设置集?

理想情况下,我的配置文件应该有多大?换句话说,在使用 Autofac 时,我需要注意哪些配置文件反模式?

提前感谢(我认为)您的回复。

音乐学家

While I'm generally finding the Autofac documentation (on the wiki) to be helpful, the sections on XML configuration and modules is a bit unclear to me. Now, I have a sample working (which I present below), but I'm unsure whether it represents a sort of bastardized approach to configuration within the context of Autofac. In particular, I'm not sure if I have more or less of what I really need in the config files and the code files.

Here's the code:

using System;
using System.IO;
using Autofac;
using Autofac.Configuration;

namespace AutofacTest.Animals
{
    interface IAnimal
    {
        void Speak ( );
    }

    abstract class Animal : IAnimal
    {
        protected TextWriter Writer
        {
            get;
            private set;
        }

        protected Animal ( TextWriter writer )
        {
            this.Writer = writer;
        }

        public abstract void Speak ( );

    }

    class Dog : Animal
    {

        public Dog ( TextWriter writer )
            : base ( writer )
        {

        }

        public override void Speak ( )
        {
            this.Writer.WriteLine ( "Arf!" );
        }
    }

    class Cat : Animal
    {
        public Cat ( TextWriter writer )
            : base ( writer )
        {

        }

        public override void Speak ( )
        {
            this.Writer.WriteLine ( "Meow" );
        }
    }

    // In actual practice, this would be in a separate assembly, right?
    class AnimalModule : Module
    {
        protected override void Load ( ContainerBuilder builder )
        {
            builder.RegisterInstance ( Console.Out ).As<TextWriter> ( ).SingleInstance ( );
            builder.Register ( d => new Dog ( d.Resolve<TextWriter> ( ) ) ).As<IAnimal> ( );
        }
    }

    class Program
    {
        static void Main ( )
        {
            Console.ForegroundColor = ConsoleColor.Yellow;

            ContainerBuilder builder = new ContainerBuilder ( );
            ConfigurationSettingsReader reader = new ConfigurationSettingsReader(); 
            builder.RegisterModule ( reader );
            //builder.RegisterModule ( new AnimalModule ( ) );
            builder.Build ( ).Resolve<IAnimal> ( ).Speak ( );
            Console.ReadKey ( );
        }
    }
}

And here's an associated config file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="autofac" type="Autofac.Configuration.SectionHandler, Autofac.Configuration"/>
    </configSections>
    <autofac defaultAssembly="AutofacTest">
        <components>
            <component
                type="AutofacTest.Animals.Cat"
                service="AutofacTest.Animals.IAnimal" />
            <component type="System.IO.StreamWriter" service="System.IO.TextWriter">
                <parameters>
                    <parameter name="path" value="C:\AutofacTest.txt"/>
                    <parameter name="append" value="false" />
                </parameters>
                <properties>
                    <property name="AutoFlush" value="true" />
                </properties>
            </component>
        </components>
        <modules>
            <module type="AutofacTest.Animals.AnimalModule, AutofacTest"/>
        </modules>
    </autofac>
</configuration>

This all works fine. The application outputs "Meow" to a text file. If I comment out the component elements, the application outputs "Arf!" to the console.

So, is everything all right here? Or is there a better way of going about this?

And I'm a little unsure about the thinking behind module-based configuration:

Am I correct that, in actual practice, modules should be in separate assemblies from the rest of the app?

Do I understand correctly that one of the chief functions of modules are to provide sets of default configuration settings for DI containers?

Ideally, how extensive should my config files be? In other words, when using Autofac, what are some config file anti-patterns for which I need to be on the lookout?

Thanks (I think) in advance for your responses.

musicologyman

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

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

发布评论

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

评论(2

岁吢2024-11-09 17:26:21

我个人的建议是谨慎使用 XML 配置。我只会将它用于您知道需要重新配置而无需重新编译的部分。例如,如果您正在构建一个可重用的库,这可能比您正在构建一个整体的 Web 应用程序要多。我尝试做的另一件事是使用以下代码使我的大多数类型可自动注册:

public static void AutoRegister(ContainerBuilder builder, params Assembly[] assemblies)
{
    builder.RegisterAssemblyTypes(assemblies)
        .Where(t => t.GetCustomAttributes(typeof (RegisterServiceAttribute), false).Any())
        .AsSelf()
        .AsImplementedInterfaces();
}

其中 RegisterServiceAttribute 是我的根项目中的一个属性:

[AttributeUsage(AttributeTargets.Class)]
[MeansImplicitUse]
public class RegisterServiceAttribute : Attribute
{
}

注意:MeansImplicitUse 来自 Resharper 。

然后,我将 [RegisterService] 放在我想要自动注册的任何类上。我至少95%的注册都是这样处理的。剩余的注册发生在调用 AutoRegister() 之后。

My personal recommendation is to use XML configuration sparingly. I would only use it for the parts that you know need to be reconfigurable without recompilation. If you're building a reusable library, this is probably more than if you are building a monolithic webapp, for example. The other thing I try to do is make most of my types auto-registerable using this code:

public static void AutoRegister(ContainerBuilder builder, params Assembly[] assemblies)
{
    builder.RegisterAssemblyTypes(assemblies)
        .Where(t => t.GetCustomAttributes(typeof (RegisterServiceAttribute), false).Any())
        .AsSelf()
        .AsImplementedInterfaces();
}

where RegisterServiceAttribute is an attribute in my root project:

[AttributeUsage(AttributeTargets.Class)]
[MeansImplicitUse]
public class RegisterServiceAttribute : Attribute
{
}

Note: MeansImplicitUse is from Resharper.

I then put [RegisterService] on any class I want to auto register. At least 95% of my registrations are handled this way. The remaining registrations happen after the call to AutoRegister().

じее2024-11-09 17:26:21

从您的示例中我不能 100% 确定预期的行为是什么 - 似乎您多次注册同一组组件,如果这是您的意图,请忽略这些建议。

  • 如果您在 XML 中注册模块,则无需注册该模块内的组件。
  • 同样,如果您在 XML 中注册模块,则无需在代码中也注册该模块。

关于“最佳实践”,我认为 Jim 提出的谨慎使用 XML 的建议是一个很好的建议。就我个人而言,我倾向于完成模块内部的所有繁重工作,然后通过 XML 注册模块以利用可在其中应用的配置。

我提出的另一个建议是仅使用 XML 来配置模块。在您的示例中,您正在组件上设置配置;如果您将参数应用于模块,然后在该模块内部根据需要将它们传递给组件,那么配置就不那么脆弱了。模块不会频繁变动,而组件需要能够在不破坏配置的情况下进行更改。

HTH,

尼克

I'm not 100% sure from your example what the expected behaviour is - it seems like you're registering the same set of components many times over, if that's your intention please ignore these suggestions.

  • If you register a module in XML, you don't also need to register the components within the module.
  • Likewise, if you register a module in XML, you don't need to also register the module in code.

Regarding 'best practices' I'd say Jim's recommendation to use XML sparingly is a good one. Personally, I tend to do all of the heavy lifting inside modules, and then register the modules via XML to take advantage of the configuration that can be applied there.

Another recommendation I'd make is to use XML to configure modules only. In your example you're setting configuration on a component; configuration is less fragile if you apply the parameters to a module, and then internal to that module pass them on to components as needed. Modules don't tend to churn very much, while components need to be able to be changed without breaking configuration.

HTH,

Nick

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文