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

MVC - Simple Injector and Attribute calling the Context (EF) Throwing exceptions

如果我启动我的应用程序并让它稳定下来,效果会很好。

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

此异常发生在调用过滤器 CultureResolver 期间,过滤器 CultureResolver 随后调用 LanguageService。在 LanguageService 中调用数据库以检索所有可用语言。

我遇到了很多不同的异常,例如:

这些异常都发生在同一个查询中,这取决于我离开第一个选项卡的时间运行。

所以它似乎类似于线程不安全代码或此查询试图在初始化上下文之前获取项目。

我有以下内容:

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

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

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

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

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

container.RegisterPerWebRequest<ICultureInitializerFilter, CultureInitializerFilter>();

您实际上是在说每个 Web 请求应该有一个实例,很可能是因为 class 依赖于 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);
    }
}

此 class 允许您添加全局过滤器,如下所示:

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

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