2012-05-23 11 views
24

का उपयोग करके एक डेटा सेट के मैक्स बनाने के लिए एमओक का उपयोग कर।एक अभिव्यक्ति <Func<,>> प्रतिबिंब

मैंने एक छोटी सहायक कक्षा बनाई है जो मुझे डेटाबेस के बजाय मेमोरी स्टोरेज में रखने की अनुमति देती है जो इकाई को हवा का परीक्षण करती है। इस तरह से मैं अपने नकली डेटा सेट से आइटम जोड़ और निकाल सकता हूं, इससे मुझे मेरी डालने का परीक्षण करने और सेवा कॉल हटाने की अनुमति मिलती है।

नकली के सेटअप के दौरान मैं एक पंक्ति है कि तो मैं प्रतिबिंब का उपयोग कर इस स्थापना चरण को पूरा करना चाहते हैं संपत्तियों की एक बहुत है निम्नलिखित

this.Setup(i => i.AcademicCycles).Returns(mockStore.GetList<AcademicCycle>()); 

मेरे नकली की तरह लग रहा है। मैंने प्रतिबिंब के माध्यम से काम कर रहे प्रक्रिया के Returns भाग में कामयाब रहा है लेकिन मैं लैम्ब्डा विधि पर Setup पर फंस गया हूं।

Setup एक

Expression<Func<GoalsModelUnitOfWork, IQueryable<AcademicCycle>>> कि i => i.AcademicCycles

से मेल खाती है लेता है और मैं इस गतिशील बनाने के लिए करना चाहते हैं। प्रतिबिंब का उपयोग करना मैं निम्नलिखित है:

संपत्ति के नाम: "AcademicCycles"

प्रकार IQueryable<AcademicCycle>

प्रकार AcademicCycle

मैं भी लैम्ब्डा बयान में i का उदाहरण है जो GoalsModelUnitOfWork

उत्तर

23

गतिशील रूप से अभिव्यक्ति बनाने के लिए कोड इस तरह होगा:

ParameterExpression parameter = Expression.Parameter(typeof (GoalsModelUnitOfWork), "i"); 
MemberExpression property = Expression.Property(parameter, "AcademicCycles"); 

var queryableType = typeof (IQueryable<>).MakeGenericType(typeof (AcademicCycle)); 
var delegateType = typeof (Func<,>).MakeGenericType(typeof (GoalsModelUnitOfWork), queryableType); 

var yourExpression = Expression.Lambda(delegateType, property, parameter); 

परिणाम इच्छित प्रकार होगा, लेकिन समस्या यह है कि Expression.Lambda() की वापसी प्रकार LambdaExpression है है और आप Expression<Func<...>> करने के लिए एक प्रकार डाली प्रदर्शन नहीं कर सकते अपने स्थापना समारोह के लिए पैरामीटर के रूप में इसे पारित करने के क्योंकि तुम डॉन ' Func के लिए सामान्य प्रकार पैरामीटर नहीं जानते हैं। तो तुम प्रतिबिंब द्वारा Setup विधि आह्वान करने के लिए भी है:

this.GetType().GetMethod("Setup", yourExpression.GetType()).Invoke(this, yourExpression); 
+1

असल Expression.Lambda का परिणाम 'अभिव्यक्ति लिए डाली जा सकती है>' यदि आप स्थिर पैरामीटर जानते हैं और वापस जाने के प्रकार: यदि मैं उस के बारे में सही हूँ, आप थोड़ा आसान बनाने में कर सकता है। आंतरिक रूप से अभिव्यक्ति। लैम्ब्डा उपयुक्त 'अभिव्यक्ति >' प्रकार का एक उदाहरण बनाता है, भले ही अभिव्यक्ति का रिटर्न प्रकार। लैम्ब्डा को कमजोर टाइप किया गया हो। – itowlson

+1

इसके अलावा मुझे नहीं लगता कि आपको मध्य दो लाइनों की आवश्यकता है। एक सरल मामले में परीक्षण से, 'var lambda = Expression.Lambda (पैरामीटर, प्रॉपर्टी)' काम करना चाहिए (अभिव्यक्ति। लैम्ब्डा प्रकार और गुणों से प्रतिनिधि प्रकार का काम करेगा)। हालांकि मेरा टेस्ट कोड आपके लिए थोड़ा अलग था और सरल प्रकार का उपयोग करता था, इसलिए आपका माइलेज भिन्न हो सकता है ...! – itowlson

2

मैंने इसे एक दरार लेने का फैसला किया और कोड के इस देवता भयंकर टुकड़े के साथ समाप्त हो गया।

मैं कोई प्रतिबिंब विशेषज्ञ नहीं हूं और यह कुछ काम करने का पहला प्रयास है। मुझे बहुत दिलचस्पी होगी कि लोगों के पास अन्य दृष्टिकोण क्या हैं, या क्या कोई रिफ्लेक्शन रैपर लाइब्रेरी इस अच्छे को बना सकता है।

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Reflection; 
using Moq; 
using Xunit; 

namespace MyExample 
{ 
    public class Tests 
    { 
     [Fact] 
     public void Test() 
     { 
      Dictionary<Type, object> data = new Dictionary<Type, object> 
      { 
       { typeof(IQueryable<Cycle>), new List<Cycle> { new Cycle { Name = "Test" } }.AsQueryable() }, 
       { typeof(IQueryable<Rider>), new List<Rider> { new Rider { Name = "1"}, new Rider { Name = "2" } }.AsQueryable() } 
      }; 

      var mock = new Mock<IDataContext>(); 
      var setup = mock.GetType().GetMethods().Single(d => d.Name == "Setup" && d.ContainsGenericParameters); 
      var param = Expression.Parameter(typeof(IDataContext), "i"); 
      foreach (var property in typeof(IDataContext).GetProperties(BindingFlags.Public | BindingFlags.Instance)) 
      { 
       // Build lambda 
       var ex = Expression.Lambda(Expression.MakeMemberAccess(param, property), param); 

       // Get generic version of the Setup method 
       var typedSetup = setup.MakeGenericMethod(property.PropertyType); 

       // Run the Setup method 
       var returnedSetup = typedSetup.Invoke(mock, new[] { ex }); 

       // Get generic version of IReturns interface 
       var iReturns = typedSetup.ReturnType.GetInterfaces().Single(d => d.Name.StartsWith("IReturns`")); 

       // Get the generic Returns method 
       var returns = iReturns.GetMethod("Returns", new Type[] { property.PropertyType }); 

       // Run the returns method passing in our data 
       returns.Invoke(returnedSetup, new[] { data[property.PropertyType] }); 
      } 

      Assert.Equal(1, mock.Object.Cycles.Count()); 
     } 
    } 

    public class Cycle 
    { 
     public string Name { get; set; } 
    } 

    public class Rider 
    { 
     public string Name { get; set; } 
    } 

    public interface IDataContext 
    { 
     IQueryable<Cycle> Cycles { get; set; } 

     IQueryable<Rider> Riders { get; set; } 
    } 
} 
2

इस विधि को लैम्ब्डा अभिव्यक्ति का निर्माण करना चाहिए। चूंकि आप प्रतिबिंब द्वारा सेटअप विधि का आह्वान कर रहे हैं, इसलिए आपको दृढ़ता से टाइप किए गए लैम्ब्डा अभिव्यक्ति की आवश्यकता नहीं है; आप किसी ऑब्जेक्ट सरणी के भाग के रूप में पारित करने के लिए जब आप Invoke फोन जा रहे हैं:

public LambdaExpression PropertyGetLambda(string parameterName, Type parameterType, string propertyName, Type propertyType) 
    { 
     var parameter = Expression.Parameter(parameterType, parameterName); 
     var memberExpression = Expression.Property(parameter, propertyName); 
     var lambdaExpression = Expression.Lambda(memberExpression, parameter); 
     return lambdaExpression; 
    } 

मुझे नहीं लगता कि आप वास्तव में पैरामीटर नाम की क्या ज़रूरत है।

public LambdaExpression PropertyGetLambda(Type parameterType, string propertyName, Type propertyType) 
    { 
     var parameter = Expression.Parameter(parameterType); 
     var memberExpression = Expression.Property(parameter, propertyName); 
     var lambdaExpression = Expression.Lambda(memberExpression, parameter); 
     return lambdaExpression; 
    } 
संबंधित मुद्दे