asp.net-mvc 未注册默认实例且无法自动确定类型
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29553485/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
No default Instance is registered and cannot be automatically determined for type
提问by Mojammel Haque
The definition of my interface is as follows:
我的接口定义如下:
public interface IApplicationSettings
{
string LoggerName { get; }
string NumberOfResultsPerPage { get; }
string EmailAddress { get; }
string Credential { get; }
}
The implementation of this interface is given below:
该接口的实现如下:
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"]; }
}
}
I also created a factory class to obtain the instance of the concrete implementation of WebConfigSettings as follows:
我还创建了一个工厂类来获取WebConfigSettings具体实现的实例如下:
public class ApplicationSettingsFactory
{
private static IApplicationSettings _applicationSettings;
public static void InitializeApplicationSettingsFactory(
IApplicationSettings applicationSettings)
{
_applicationSettings = applicationSettings;
}
public static IApplicationSettings GetApplicationSettings()
{
return _applicationSettings;
}
}
Then I resolved dependency as follows:
然后我解决了依赖关系如下:
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>());
}
}
Now when i running my application it throw me following exception:
现在,当我运行我的应用程序时,它会抛出以下异常:
Exception has been thrown by the target of an invocation.
and the Inner Exception is
内部异常是
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
I am using StructureMap for MVC5
我正在为 MVC5 使用 StructureMap
采纳答案by Mojammel Haque
Thanks every one for responses. I found my solution. The solution is instead of using Default Registry I created another class for resolve the dependencies. Inside the class I used
感谢每一位的回复。我找到了我的解决方案。解决方案不是使用默认注册表,而是创建了另一个类来解决依赖关系。在我使用的课程中
ObjectFactory.Initialize(x =>
{
x.AddRegistry<ControllerRegistry>();
});
instead of
代替
IContainer Initialize() {
return new Container(c => c.AddRegistry<ControllerRegistry>());
}
Then inside ControllerRegistry I resolved dependencies as follows:
然后在 ControllerRegistry 中,我按如下方式解析了依赖项:
// Application Settings
For<IApplicationSettings>().Use<WebConfigApplicationSettings>();
Then I called that class inside Global.asaxas follows:
然后我在Global.asax 中调用该类,如下所示:
Bootstrap.ConfigureDependencies();
Finally inside Global.asaxI resolved dependency for Factory class as follows:
最后在Global.asax 中,我解决了 Factory 类的依赖关系,如下所示:
ApplicationSettingsFactory.InitializeApplicationSettingsFactory
(ObjectFactory.GetInstance<IApplicationSettings>());
My entire code is given below:
我的整个代码如下:
Bootstrap class (newly created)
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
全球.asax
Bootstrap.ConfigureDependencies();
ApplicationSettingsFactory.InitializeApplicationSettingsFactory
(ObjectFactory.GetInstance<IApplicationSettings>());
回答by Joseph Woodward
The reason your code isn't working is because when you call ObjectFactory.GetInstance<IApplicationSettings>(), your registry hasn't been registered and thus, StructureMap's configuration is incomplete.
您的代码不起作用的原因是,当您调用 时ObjectFactory.GetInstance<IApplicationSettings>(),您的注册表尚未注册,因此 StructureMap 的配置不完整。
I believe what you're trying to do is the following (tested and works):
我相信您要做的是以下内容(经过测试并有效):
public class ApplicationSettingsFactory
{
public ApplicationSettingsFactory(WebConfigApplicationSettings applicationSettings)
{
_applicationSettings = applicationSettings;
}
private static IApplicationSettings _applicationSettings;
public IApplicationSettings GetApplicationSettings()
{
return _applicationSettings;
}
}
With your registry configured like this:
您的注册表配置如下:
public DefaultRegistry() {
Scan(scan => {
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.With(new ControllerConvention());
});
this.For<IApplicationSettings>().Use(ctx => ctx.GetInstance<ApplicationSettingsFactory>().GetApplicationSettings());
}
回答by Steven
I can't really tell you why your registration fails in StructureMap, but if you allow me, I would like to feedback on your design.
我真的不能告诉你为什么你在 StructureMap 中注册失败,但如果你允许我,我想反馈你的设计。
Your design and code violates a few basic principles:
您的设计和代码违反了一些基本原则:
You are violating the Interface Segregation Princple(ISP).
The ISP describes that interfaces should be narrow (role interfaces) and should not contain more members than a consumer uses. You however defined an application wide
IApplicationSettingsinterface and your intention is to inject into any consumer that needs some configuration settings. Changes are really slim however that there is a consumer that actually needs all settings. This forces the consumer to depend on all members, it makes the API more complex, while it just needs one.You are violating the Open/Closed Principle(OCP).
The OCP describes that it should be possible to add new features without making changes to existing classes in the code base. You will however find yourself updating the
IApplicationSettingsinterface and its implementations (you will probably have a fake/mock implementation as well) every time a new setting is added.Configuration values aren't read at startup, which makes it harder to verify the application's configuration.
When a consumer makes a call to a property of your
IApplicationSettingsabstraction, you are forwarding the call to theConfigurationManager.AppSettings. This means that if the value isn't available or incorrectly formatted, the application will fail at runtime. Since some of your configuration values will only be used in certain cases, this forces you to test every such case after you deployed the application to find out whether the system is configured correctly.
您违反了接口隔离原则(ISP)。
ISP 描述接口应该是狭窄的(角色接口)并且不应该包含比消费者使用的更多的成员。然而,您定义了一个应用程序范围的
IApplicationSettings接口,您的目的是注入任何需要一些配置设置的使用者。变化真的很小,但是有一个消费者实际上需要所有设置。这迫使消费者依赖所有成员,这使得 API 更加复杂,而它只需要一个。您违反了开放/封闭原则(OCP)。
OCP 描述应该可以在不更改代码库中现有类的情况下添加新功能。但是,
IApplicationSettings每次添加新设置时,您都会发现自己更新了界面及其实现(您可能还会有一个假/模拟实现)。启动时不会读取配置值,这使得验证应用程序的配置变得更加困难。
当消费者调用
IApplicationSettings抽象的属性时,您将调用转发到ConfigurationManager.AppSettings. 这意味着如果该值不可用或格式不正确,应用程序将在运行时失败。由于您的某些配置值将仅在某些情况下使用,因此这会迫使您在部署应用程序后测试每个此类情况,以查明系统是否配置正确。
Solution
解决方案
The solution to these problems is actually quite simple:
这些问题的解决方法其实很简单:
- Load configuration values at start-up.
- Inject configuration values directly into a component that needs that exact value.
- 在启动时加载配置值。
- 将配置值直接注入需要该确切值的组件中。
Loading the configuration values directly at start-up, allows the application to fail fast in case of a configuration error, and prevents the configuration from being read over and over again needlessly.
在启动时直接加载配置值,允许应用程序在配置错误的情况下快速失败,并防止配置被不必要地反复读取。
Injecting configuration values directly into a component, prevents that component from having to depend on an ever-changing interface. It makes it really clear what a component is depending upon, and bakes this information in during application start-up.
将配置值直接注入到组件中,可以防止该组件不得不依赖于不断变化的接口。它使组件所依赖的内容变得非常清楚,并在应用程序启动期间将这些信息放入其中。
This doesn't mean though that you can't use some sort of ApplicationSettingsDTO. Such DTO is exactly what I use in my applications. This basically looks as follows:
这并不意味着您不能使用某种ApplicationSettingsDTO。这种 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;
}
In the code above you'll see that the creation of the ApplicationSettingsDTO is split from the configuration of the container. This way I can test my DI configuration inside an integration test, where the start-up projects configuration file is not available.
在上面的代码中,您将看到ApplicationSettingsDTO的创建与容器的配置分离。这样我就可以在集成测试中测试我的 DI 配置,其中启动项目配置文件不可用。
Also note that I supply the configuration values directly to the constructors of components that require it.
另请注意,我将配置值直接提供给需要它的组件的构造函数。
You might be skeptic, because it might seem to pollute your DI configuration, because you have dozens of objects that require to be set with the same configuration value. For instance, your application might have dozens of repositories and each repository needs a connection string.
您可能会持怀疑态度,因为它似乎会污染您的 DI 配置,因为您有数十个对象需要使用相同的配置值进行设置。例如,您的应用程序可能有几十个存储库,每个存储库都需要一个连接字符串。
But my experience is that is you have many components that need the same configuration value; you are missing an abstraction. But don't create an IConnectionStringSettingsclass, because that would recreate the same problem again and in this case you aren't really making an abstraction. Instead, abstract the behavior that uses this configuration value! In the case of the connection string, create an IConnectionFactoryor IDbContextFactoryabstraction that allows creation of SqlConnection's or DbContextclasses. This completely hides the fact that there is a connection string from any consumer, and allows them to call connectionFactory.CreateConnection()instead of having to fiddle around with the connection and the connection string.
但我的经验是,您有许多组件需要相同的配置值;你缺少一个抽象。但是不要创建一个IConnectionStringSettings类,因为那会再次产生同样的问题,在这种情况下,你并没有真正进行抽象。相反,抽象使用此配置值的行为!在连接字符串的情况下,创建允许创建's 或类的IConnectionFactoryorIDbContextFactory抽象。这完全隐藏了任何消费者都有一个连接字符串的事实,并允许他们调用而不必摆弄连接和连接字符串。SqlConnectionDbContextconnectionFactory.CreateConnection()
My experience is that makes the application code much cleaner, and improves the verifiability of the application.
我的经验是,这使应用程序代码更加简洁,并提高了应用程序的可验证性。

