没有注册默认实例,无法自动确定类型

No default Instance is registered and cannot be automatically determined for type

我的接口定义如下:

public interface IApplicationSettings
{
   string LoggerName { get; }
   string NumberOfResultsPerPage { get; }
   string EmailAddress { get; }
   string Credential { get; }
}

该接口的实现如下:

public class WebConfigApplicationSettings : IApplicationSettings
    {
        public string LoggerName
        {
            get { return ConfigurationManager.AppSettings["LoggerName"]; }
        }

        public string NumberOfResultsPerPage
        {
            get { return ConfigurationManager.AppSettings["NumberOfResultsPerPage"]; }
        }

        public string EmailAddress
        {
            get { return ConfigurationManager.AppSettings["EmailAddress"]; }
        }

        public string Credential
        {
            get { return ConfigurationManager.AppSettings["Credential"]; }
        }
    }

我还创建了一个工厂class来获取WebConfigSettings具体实现的实例如下:

public class ApplicationSettingsFactory
    {
        private static IApplicationSettings _applicationSettings;

        public static void InitializeApplicationSettingsFactory(
                                      IApplicationSettings applicationSettings)
        {
            _applicationSettings = applicationSettings;
        }

        public static IApplicationSettings GetApplicationSettings()
        {
            return _applicationSettings;
        }
    }

然后我解决依赖如下:

public class DefaultRegistry : Registry {

        public DefaultRegistry() {
            Scan(
                scan => {
                    scan.TheCallingAssembly();
                    scan.WithDefaultConventions();
                    scan.With(new ControllerConvention());
                });


             For<IApplicationSettings>().Use<WebConfigApplicationSettings>();

             ApplicationSettingsFactory.InitializeApplicationSettingsFactory
                                   (ObjectFactory.GetInstance<IApplicationSettings>());

        }
    }

现在,当我 运行 我的应用程序时,它抛出以下异常:

Exception has been thrown by the target of an invocation.

内部异常是

No default Instance is registered and cannot be automatically determined for type 'Shoppingcart.Infrastructure.Configuration.IApplicationSettings'\r\n\r\nThere is no configuration specified for Shoppingcart.Infrastructure.Configuration.IApplicationSettings\r\n\r\n1.) Container.GetInstance(Shoppingcart.Infrastructure.Configuration.IApplicationSettings)\r\n

我正在为 MVC5 使用 StructureMap

我真的不能告诉你为什么你在 StructureMap 中注册失败,但如果你允许我,我想对你的设计提出反馈。

您的设计和代码违反了一些基本原则:

  1. 您违反了 Interface Segregation Princple (ISP)。

    ISP 描述接口应该是窄的(角色接口)并且包含的​​成员不应超过消费者使用的数量。然而,您定义了一个应用程序范围的 IApplicationSettings 接口,您的意图是注入任何需要一些配置设置的消费者。变化非常小,但实际上有一个用户需要所有设置。这迫使消费者依赖所有成员,这使得 API 更复杂,而它只需要一个。

  2. 您违反了 Open/Closed Principle (OCP)。

    OCP 描述应该可以在不更改代码库中现有 classes 的情况下添加新功能。但是,您会发现每次添加新设置时,您都会更新 IApplicationSettings 接口及其实现(您可能也会有一个 fake/mock 实现)。

  3. 启动时不读取配置值,这使得验证应用程序的配置变得更加困难。

    当消费者调用您的 IApplicationSettings 抽象的 属性 时,您会将调用转发给 ConfigurationManager.AppSettings。这意味着如果值不可用或格式不正确,应用程序将在运行时失败。由于您的某些配置值只会在某些情况下使用,这迫使您在部署应用程序后对每个此类情况进行测试,以确定系统配置是否正确。

解决方案

解决这些问题其实很简单:

  1. 在启动时加载配置值。
  2. 将配置值直接注入到需要该准确值的组件中。

在启动时直接加载配置值,允许应用程序在配置错误的情况下快速失败,并防止不必要地一遍又一遍地读取配置。

将配置值直接注入组件,可以防止该组件依赖于不断变化的接口。它使组件所依赖的内容变得非常清楚,并在应用程序启动期间将此信息融入其中。

这并不意味着您不能使用某种 ApplicationSettings DTO。这样的 DTO 正是我在我的应用程序中使用的。这基本上如下所示:

public static Container Bootstrap() {
    return Bootstrap(new ApplicationSettings
    {
        LoggerName = ConfigurationManager.AppSettings["LoggerName"],
        NumberOfResultsPerPage = int.Parse(
            ConfigurationManager.AppSettings["NumberOfResultsPerPage"]),
        EmailAddress = new MailAddres(
            ConfigurationManager.AppSettings["EmailAddress"]),
        Credential = ConfigurationManager.AppSettings["Credential"],
    });
}

public static Container Bootstrap(ApplicationSettings settings) {
    var container = new Container();

    container.RegisterSingle<ILogger>(
        new SmtpLogger(settings.LoggerName, settings.EmailAddress));

    container.RegisterSingle<IPagingProvider>(
        new PagingProvider(settings.NumberOfResultsPerPage));

    // Etc

    return container;
}

在上面的代码中,您会看到 ApplicationSettings DTO 的创建是从容器的配置中分离出来的。这样我就可以在集成测试中测试我的 DI 配置,其中启动项目配置文件不可用。

另请注意,我将配置值直接提供给需要它的组件的构造函数。

您可能会怀疑,因为它可能会污染您的 DI 配置,因为您有许多对象需要设置相同的配置值。例如,您的应用程序可能有几十个存储库,每个存储库都需要一个连接字符串。

但我的经验是你有很多组件需要相同的配置值;你缺少一个抽象。但是不要创建 IConnectionStringSettings class,因为那样会再次重现同样的问题,在这种情况下,您实际上并没有进行抽象。相反,抽象使用此配置值的行为!对于连接字符串,创建允许创建 SqlConnectionDbContext class 的 IConnectionFactoryIDbContextFactory 抽象。这完全隐藏了任何消费者都有连接字符串的事实,并允许他们调用 connectionFactory.CreateConnection() 而不是必须 fiddle 处理连接和连接字符串。

我的经验是使应用程序代码更简洁,并提高应用程序的可验证性。

您的代码无法运行的原因是,当您调用 ObjectFactory.GetInstance<IApplicationSettings>() 时,您的注册表尚未注册,因此 StructureMap 的配置不完整。

我相信您正在尝试执行以下操作(已测试并有效):

public class ApplicationSettingsFactory
{
    public ApplicationSettingsFactory(WebConfigApplicationSettings applicationSettings)
    {
        _applicationSettings = applicationSettings;
    }

    private static IApplicationSettings _applicationSettings;

    public IApplicationSettings GetApplicationSettings()
    {
        return _applicationSettings;
    }
}

您的注册表配置如下:

public DefaultRegistry() {

    Scan(scan => {
         scan.TheCallingAssembly();
         scan.WithDefaultConventions();
         scan.With(new ControllerConvention());
    });

    this.For<IApplicationSettings>().Use(ctx => ctx.GetInstance<ApplicationSettingsFactory>().GetApplicationSettings());

}

感谢大家的回复。我找到了我的解决方案。解决方案不是使用默认注册表,而是创建了另一个 class 来解决依赖关系。在 class 里面我用了

ObjectFactory.Initialize(x =>
            {
                x.AddRegistry<ControllerRegistry>();

            }); 

而不是

IContainer Initialize() {
            return new Container(c => c.AddRegistry<ControllerRegistry>());
        }

然后在 ControllerRegistry 中,我按如下方式解决依赖关系:

// Application Settings                 
For<IApplicationSettings>().Use<WebConfigApplicationSettings>();

然后我在 Global.asax 中调用 class 如下:

Bootstrap.ConfigureDependencies();

最后在 Global.asax 中,我解决了工厂 class 的依赖关系,如下所示:

 ApplicationSettingsFactory.InitializeApplicationSettingsFactory
                                  (ObjectFactory.GetInstance<IApplicationSettings>());

我的全部代码如下:

Bootstrap class(新建)

public class Bootstrap
    {
        public static void ConfigureDependencies()
        {
            ObjectFactory.Initialize(x =>
            {
                x.AddRegistry<ControllerRegistry>();

            });
        }
        public class ControllerRegistry : Registry
        {
            public ControllerRegistry()
            {

                // Application Settings                 
                For<IApplicationSettings>().Use<WebConfigApplicationSettings>();

           }
        }

    }

Global.asax

Bootstrap.ConfigureDependencies();

ApplicationSettingsFactory.InitializeApplicationSettingsFactory
                                  (ObjectFactory.GetInstance<IApplicationSettings>());