MVC - 简单的注入器和属性调用上下文(EF)抛出异常

时间:2016-07-15 13:20:04

标签: asp.net-mvc entity-framework dependency-injection ioc-container simple-injector

如果我开始申请并让它解决,那就很有效。

但是,当我调试我的应用程序时,如果我在初始化任何内容之前关闭浏览器选项卡,然后调用另一个像localhost:81/Home/Test那样,则会从DB(EF)检索数据时抛出异常。

在调用过滤器CultureResolver期间会发生此异常,然后调用LanguageService。在LanguageService内,有一个DB要求检索所有可用的语言。

我有很多不同的例外,例如:

  • 创建模型时无法使用上下文。这个 如果在内部使用上下文,则可能抛出异常 OnModelCreating方法或者如果访问相同的上下文实例 并发多个线程。注意实例成员 DbContext和相关类不保证是线程安全的。
  • 未将对象引用设置为对象的实例。
  • 基础提供程序在Open上失败。

这些异常发生在同一个查询中,它取决于我离开第一个标签运行的时间。

所以它看起来像是线程不安全的代码或者这个查询在初始化Context之前尝试获取项目。

我有以下几点:

SimpleInjectorInitializer.cs

public static class SimpleInjectorInitializer
{
    /// <summary>Initialize the container and register it as MVC3 Dependency Resolver.</summary>
    public static void Initialize()
    {
        var container = new Container();
        container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();

        InitializeContainer(container);
        container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
        container.Verify();
        DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));

        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters, container);
    }

    private static void InitializeContainer(Container container)
    {
        container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();

        /* Bindings... */

        container.RegisterPerWebRequest<IAjaxMessagesFilter, AjaxMessagesFilter>();
        container.RegisterPerWebRequest<ICustomErrorHandlerFilter, CustomErrorHandlerFilter>();
        container.RegisterPerWebRequest<ICultureInitializerFilter, CultureInitializerFilter>();
    }
}

FilterConfig.cs

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters, Container container)
    {
        filters.Add(container.GetInstance<ICultureInitializerFilter>());
        filters.Add(container.GetInstance<ICustomErrorHandlerFilter>());
        filters.Add(container.GetInstance<IAjaxMessagesFilter>());
    }
}

CultureResolver.cs

public class CultureResolver : ICultureResolver
{
    ILanguageService Service;
    public CultureResolver(ILanguageService Service)
    {
        this.Service = Service;
    }

    public string Resolve(string CultureCode)
    {
        // Get the culture by name or code (pt / pt-pt)
        ILanguageViewModel language = Service.GetByNameOrCode(CultureCode);

        if (language == null)
        {
            // Get the default language
            language = Service.GetDefault();
        }

        return language.Code;
    }
}

LanguageService.cs

public class LanguageService : ILanguageService
{
    IMembership membership;
    ChatContext context;
    ILanguageConverter converter;

    public LanguageService(
            ChatContext context,
            IMembership membership,
            ILanguageConverter converter
        )
    {
        this.membership = membership;
        this.context = context;
        this.converter = converter;
    }

    public virtual ILanguageViewModel GetByNameOrCode(string Text)
    {
        string lowerText = Text.ToLower();
        string lowerSmallCode = "";

        int lowerTextHiphen = lowerText.IndexOf('-');
        if (lowerTextHiphen > 0)
            lowerSmallCode = lowerText.Substring(0, lowerTextHiphen);

        Language item = this.context
                            .Languages
                            .FirstOrDefault(x => x.Code.ToLower() == lowerText
                                                 || x.SmallCode.ToLower() == lowerText
                                                 || x.SmallCode == lowerSmallCode);
        return converter.Convert(item);
    }

    public virtual ILanguageViewModel GetDefault()
    {
        Language item = this.context
                            .Languages
                            .FirstOrDefault(x => x.Default);
        return converter.Convert(item);
    }
}

这是给我例外的查询

Language item = this.context
                    .Languages
                    .FirstOrDefault(x => x.Code.ToLower() == lowerText
                                         || x.SmallCode.ToLower() == lowerText
                                         || x.SmallCode == lowerSmallCode);

1 个答案:

答案 0 :(得分:1)

MVC和Web API中的全局过滤器是单例。在应用程序的生命周期中,只有一个此类过滤器的实例。当您查看以下代码时,这一点就变得很明显了:

filters.Add(container.GetInstance<ICultureInitializerFilter>());

在这里,您可以从容器中解析一次过滤器,并将其存储在容器的生命周期内。

但是,您已使用以下方式将此类型注册为Scoped

container.RegisterPerWebRequest<ICultureInitializerFilter, CultureInitializerFilter>();

您实际上是说每个Web请求应该有一个实例,很可能是因为该类依赖于DbContext,这不是线程安全的。

要允许过滤器具有依赖关系,您应该将它们设为humble objects,或者将它们包装在可以调用它们的简单对象中。例如,您可以创建以下操作过滤器:

public sealed class GlobalActionFilter<TActionFilter> : IActionFilter 
    where TActionFilter : class, IActionFilter
{
    private readonly Container container;
    public GlobalActionFilter(Container container) { this.container = container; }

    public void OnActionExecuted(ActionExecutedContext filterContext) {
        container.GetInstance<TActionFilter>().OnActionExecuted(filterContext);
    }

    public void OnActionExecuting(ActionExecutingContext filterContext) {
        container.GetInstance<TActionFilter>().OnActionExecuting(filterContext);
    }
}

此类允许您按如下方式添加全局过滤器:

filters.Add(new GlobalActionFilter<ICultureInitializerFilter>(container));
filters.Add(new GlobalActionFilter<ICustomErrorHandlerFilter>(container));
filters.Add(new GlobalActionFilter<IAjaxMessagesFilter>(container));

GlovalActionFilter<T>将回调到容器中,以便在每次调用时解析提供的类型。这可以防止依赖变为captive,从而防止您遇到的问题。