سؤال

أردنا ترقية مشاريعنا من ASP.NET MVC 2 إلى 3. نجحت معظم اختباراتنا ، ولكن هناك بعض الفشل ValueProviderFactories.Factories.GetValueProvider(context).

فيما يلي فئة اختبار بسيطة تثير المشكلة.

[TestFixture]
public class FailingTest
{
  [Test]
  public void Test()
  {
    var type = typeof(string);
    // any controller
    AuthenticationController c = new AuthenticationController();
    var httpContext = new Mock<HttpContextBase>();
    var context = c.ControllerContext = new ControllerContext(httpContext.Object, new RouteData(), c);

    IModelBinder converter = ModelBinders.Binders.GetBinder(type);
    var bc = new ModelBindingContext
    {
      ModelName = "testparam",
      ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, type),
      ValueProvider = ValueProviderFactories.Factories.GetValueProvider(context)
    };
    Console.WriteLine(converter.BindModel(context, bc));
  }
}

استثناء "لم يتم تعيين مرجع الكائن على مثيل لكائن." يتم إلقاؤه عندما ValueProviderFactories.Factories.GetValueProvider(context) يسمى. يبدو Stacktrace مثل هذا:

Microsoft.Web.Infrastructure.dll!Microsoft.Web.Infrastructure.DynamicValidationHelper.ValidationUtility.CollectionReplacer.GetUnvalidatedCollections(System.Web.HttpContext context) + 0x23 bytes   
Microsoft.Web.Infrastructure.dll!Microsoft.Web.Infrastructure.DynamicValidationHelper.ValidationUtility.GetUnvalidatedCollections(System.Web.HttpContext context, out System.Collections.Specialized.NameValueCollection form, out System.Collections.Specialized.NameValueCollection queryString, out System.Collections.Specialized.NameValueCollection headers, out System.Web.HttpCookieCollection cookies) + 0xbe bytes    
System.Web.WebPages.dll!System.Web.Helpers.Validation.Unvalidated(System.Web.HttpRequest request) + 0x73 bytes  
System.Web.WebPages.dll!System.Web.Helpers.Validation.Unvalidated(System.Web.HttpRequestBase request) + 0x25 bytes  
System.Web.Mvc.DLL!System.Web.Mvc.FormValueProviderFactory..ctor.AnonymousMethod__0(System.Web.Mvc.ControllerContext cc) + 0x5a bytes   
System.Web.Mvc.DLL!System.Web.Mvc.FormValueProviderFactory.GetValueProvider(System.Web.Mvc.ControllerContext controllerContext) + 0xa0 bytes    
System.Web.Mvc.DLL!System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider.AnonymousMethod__7(System.Web.Mvc.ValueProviderFactory factory) + 0x4a bytes  
System.Core.dll!System.Linq.Enumerable.WhereSelectEnumerableIterator<System.Web.Mvc.ValueProviderFactory,<>f__AnonymousType2<System.Web.Mvc.ValueProviderFactory,System.Web.Mvc.IValueProvider>>.MoveNext() + 0x24d bytes   
System.Core.dll!System.Linq.Enumerable.WhereSelectEnumerableIterator<<>f__AnonymousType2<System.Web.Mvc.ValueProviderFactory,System.Web.Mvc.IValueProvider>,System.Web.Mvc.IValueProvider>.MoveNext() + 0x2ba bytes 
mscorlib.dll!System.Collections.Generic.List<System.Web.Mvc.IValueProvider>.List(System.Collections.Generic.IEnumerable<System.Web.Mvc.IValueProvider> collection) + 0x1d8 bytes    
System.Core.dll!System.Linq.Enumerable.ToList<System.Web.Mvc.IValueProvider>(System.Collections.Generic.IEnumerable<System.Web.Mvc.IValueProvider> source) + 0xb5 bytes 
System.Web.Mvc.DLL!System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(System.Web.Mvc.ControllerContext controllerContext) + 0x24d bytes 
test.DLL!FailingTest.Test() Line 31 + 0xf9 bytes    C#

أردت أن أعرف السبب في أنه يرمي الاستثناء ورأيت:

public static ValidationUtility.UnvalidatedCollections GetUnvalidatedCollections(HttpContext context)
{
    return (ValidationUtility.UnvalidatedCollections) context.Items[_unvalidatedCollectionsKey];
}

لذلك ، هل عدنا في الماضي عندما كنا نعتمد على HttpContext.Current؟ كيف تدور حولها؟

هل كانت مفيدة؟

المحلول

يمكن حل هذا بسهولة عن طريق prox-ing valueproviders الذين يصلون إلى httpcontext إلى واحد يتجاهله.

لقد شرحت كل شيء في منشور مدونتي: إجراءات اختبار الوحدة مع ValueProviderFactories في ASP.NET MVC3.

المفتاح هو هذا الرمز:

public static class ValueProviderFactoresExtensions {
    public static ValueProviderFactoryCollection ReplaceWith<TOriginal>(this ValueProviderFactoryCollection factories, Func<ControllerContext, NameValueCollection> sourceAccessor) {
        var original = factories.FirstOrDefault(x => typeof(TOriginal) == x.GetType());
        if (original != null) {
            var index = factories.IndexOf(original);
            factories[index] = new TestValueProviderFactory(sourceAccessor);
        }
        return factories;
    }

    class TestValueProviderFactory : ValueProviderFactory {
        private readonly Func<ControllerContext, NameValueCollection> sourceAccessor;


        public TestValueProviderFactory(Func<ControllerContext, NameValueCollection> sourceAccessor) {
            this.sourceAccessor = sourceAccessor;
        }


        public override IValueProvider GetValueProvider(ControllerContext controllerContext) {
            return new NameValueCollectionValueProvider(sourceAccessor(controllerContext), CultureInfo.CurrentCulture);
        }
    }        
}

لذلك يمكن استخدامه على النحو التالي:

ValueProviderFactories.Factories
    .ReplaceWith<FormValueProviderFactory>(ctx => ctx.HttpContext.Request.Form)
    .ReplaceWith<QueryStringValueProviderFactory>(ctx => ctx.HttpContext.Request.QueryString);

كان من السهل جدا :)

تحديث: كما ذكر في التعليقات ، يجب أن تتذكر:

  1. تعيين ctx.HttpContext.Request.ContentType خاصية لبعض القيمة غير الفريدة ، وإلا فإن JSONVALUEPROVIDERFACTORY سوف يلقي استثناء. أفضل إنشاء وهمية وضبط القيمة الافتراضية هناك.
  2. استبدال HttpFileCollectionValueProviderFactory كما يمكن استخدامه أثناء الربط.
  3. احترس من التبعيات الأخرى التي قد تكون لديك في المشروع.

نصائح أخرى

يجب ألا تتصل بـ valueproviderfactories.factories أو modelbinders.binders أو أي ملحق ثابت آخر من داخل اختبار الوحدة. هذا هو بالضبط السبب في وجود modelBindingContext.valueprovider - بحيث يمكنك توفير ivalueprovider من الخلق الخاص بك بدلاً من الاعتماد على الافتراضات الثابتة (التي تفترض أن خط أنابيب MVC يعمل).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top