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

时间:2015-04-10 04:37:04

标签: asp.net-mvc ioc-container structuremap

我的界面定义如下:

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"]; }
        }
    }

我还创建了一个工厂类来获取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

我正在使用StructureMap for MVC5

3 个答案:

答案 0 :(得分:6)

您的代码无法正常工作的原因是,当您致电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());

}

答案 1 :(得分:4)

感谢每一位回复者。我找到了解决方案。解决方案是使用Default Registry而不是使用Default Registry我创建了另一个类来解决依赖关系。在课堂上我用了

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

            }); 

而不是

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

然后在ControllerRegistry内部我解决了依赖关系,如下所示:

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

然后我在 Global.asax 中调用了该类,如下所示:

Bootstrap.ConfigureDependencies();

最后在 Global.asax 内,我解决了Factory类的依赖关系,如下所示:

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

我的整个代码如下:

Bootstrap类(新创建)

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>());

答案 2 :(得分:2)

我无法告诉您为什么您的注册在StructureMap中失败,但如果您允许我,我想反馈您的设计。

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

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

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

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

    OCP描述了应该可以添加新功能而无需更改代码库中的现有类。然而,每次添加新设置时,您都会发现自己正在更新IApplicationSettings接口及其实现(您可能也会有假/模拟实现)。

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

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

  4. <强>解决方案

    解决这些问题实际上非常简单:

    1. 启动时加载配置值。
    2. 将配置值直接注入需要该值的组件。
    3. 在启动时直接加载配置值,允许应用程序在发生配置错误时快速失败,并防止配置被不必要地一遍又一遍地重复读取。

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

      这并不意味着您无法使用某种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类,因为这会再次重现同样的问题,在这种情况下,你并没有真正做出抽象。相反,抽象使用此配置值的行为!对于连接字符串,请创建IConnectionFactoryIDbContextFactory抽象,以允许创建SqlConnectionDbContext类。这完全隐藏了这样一个事实,即任何消费者都有一个连接字符串,并允许他们调用connectionFactory.CreateConnection(),而不必乱用连接和连接字符串。

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

相关问题