从参数中的查询返回查询的方法

时间:2018-11-03 13:10:51

标签: c# .net entity-framework linq reflection

演示文稿

我有一个带有某些属性的ContactProfileModel实体类:

  • 名字
  • 姓氏
  • 出生日期等。

我还有其他拥有ContactProfileModel外键的实体。示例:RegistrationModel.Contact

需要

我想创建一个具有以下签名的方法:

public static Expression<Func<TModel, string>> Contact<TModel>(Expression<Func<TModel, ContactProfileModel>> contact)

并以这种方式使用它:

DisplayQuery.Contact<RegistrationModel>(m => m.ContactProfile))

相当于

m => m.ContactProfile.FirstName + " " + m.ContactProfile.FirstName + " " + m.ContactProfile.BirthDate.ToShortTimeString()

客观

目标是返回一个linq查询,其中result是一个字符串,其中包含联系人的不同信息。例如:“ John Doe(10/10/90)”

注意

我已经与一些告诉我使用Expression.CallExpression.Property的人进行了讨论,但不幸的是,我没有足够的知识来正确使用它。

在这里,我没有太多细节就公开了我的问题,但是我有理由仅以此方式创建方法。

谢谢。

2 个答案:

答案 0 :(得分:2)

这是一个完整的工作实现:代码运行并输出您期望的结果。

我的时间略短,所以我将其保留为原样。如果您想澄清,请在评论中提出,我会尽力回答。

public class Program
{
    private static readonly MethodInfo stringConcatMethod = typeof(string).GetMethod("Concat", new[] { typeof(string[]) });
    private static readonly MethodInfo toShortTimeStringMethod = typeof(DateTime).GetMethod("ToShortTimeString");
    private static readonly PropertyInfo firstNameProperty = typeof(ContactProfileModel).GetProperty("FirstName");
    private static readonly PropertyInfo lastNameProperty = typeof(ContactProfileModel).GetProperty("LastName");
    private static readonly PropertyInfo birthDateProperty = typeof(ContactProfileModel).GetProperty("BirthDate");

    public static void Main()
    {
        var result = Contact<RegistrationModel>(x => x.ContactProfile);

        // Test it
        var model = new RegistrationModel()
        {
            ContactProfile = new ContactProfileModel()
            {
                FirstName = "First",
                LastName = "Last",
                BirthDate = DateTime.Now,
            }
        };
        var str = result.Compile()(model);
    }

    public static Expression<Func<TModel, string>> Contact<TModel>(Expression<Func<TModel, ContactProfileModel>> contact)
    {
        // We've been given a LambdaExpression. It's got a single
        // parameter, which is the 'x' above, and its body
        // should be a MemberExpression accessing a property on
        // 'x' (you might want to check this and throw a suitable
        // exception if this isn't the case). We'll grab the
        // body of the LambdaExpression, and use that as the
        // 'm.ContactProfile' expression in your question. 
        // At the end, we'll construct a new LambdaExpression
        // with our body. We need to use the same ParameterExpression
        // given in this LambdaExpression.
        var modelParameter = contact.Parameters[0];
        var propertyAccess = (MemberExpression)contact.Body;

        // <contact>.FirstName
        var firstNameAccess = Expression.Property(propertyAccess, firstNameProperty);
        // <contact>.LastName
        var lastNameAccess = Expression.Property(propertyAccess, lastNameProperty);
        // <contact>.BirthDate
        var birthDateAccess = Expression.Property(propertyAccess, birthDateProperty);
        // <contact>.BirthDate.ToShortTimeString()
        var birthDateShortTimeStringCall = Expression.Call(birthDateAccess, toShortTimeStringMethod);

        // string.Concat(new string[] { <contact>.FirstName, " ", etc }
        var argsArray = Expression.NewArrayInit(typeof(string), new Expression[]
        {
            firstNameAccess,
            Expression.Constant(" "),
            lastNameAccess,
            Expression.Constant(" "),
            birthDateShortTimeStringCall
        });
        var concatCall = Expression.Call(stringConcatMethod, argsArray);

        // Turn it all into a LambdaExpression
        var result = Expression.Lambda<Func<TModel, string>>(concatCall, modelParameter);
        // Note: if you inspect 'result.DebugView' in a debugger at this 
        // point, you'll see a representation of the expression we've built
        // up above, which is useful for figuring out where things have gone
        // wrong.
        return result;
    }
}

public class ContactProfileModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
}

public class RegistrationModel
{
    public ContactProfileModel ContactProfile { get; set; }
}

可能是EF不喜欢对String.Concat的呼叫-在这种情况下,您可能不得不在其中使用一组Expression.Add的呼叫。

答案 1 :(得分:0)

关于StackOverflow的第一个答案,请客气;)

我试图解决该问题,但是表达式并不容易使用。感谢canton7的答案。如果您要在表达式中使用android { compileSdkVersion 27 buildToolsVersion '27.0.3' defaultConfig { applicationId "com.yojma" minSdkVersion 21 targetSdkVersion 27 versionCode 3 versionName "3.0" ndk { abiFilters "armeabi-v7a", "x86" } } signingConfigs { release { if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) { storeFile file(MYAPP_RELEASE_STORE_FILE) storePassword MYAPP_RELEASE_STORE_PASSWORD keyAlias MYAPP_RELEASE_KEY_ALIAS keyPassword MYAPP_RELEASE_KEY_PASSWORD } } } splits { abi { reset() enable enableSeparateBuildPerCPUArchitecture universalApk false // If true, also generate a universal APK include "armeabi-v7a", "x86" } } buildTypes { release { minifyEnabled enableProguardInReleaseBuilds proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" signingConfig signingConfigs.release } } // applicationVariants are e.g. debug, release applicationVariants.all { variant -> variant.outputs.each { output -> // For each separate APK per architecture, set a unique version code as described here: // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits def versionCodes = ["armeabi-v7a":1, "x86":2] def abi = output.getFilter(OutputFile.ABI) if (abi != null) { // null for the universal-debug, universal-release variants output.versionCodeOverride = versionCodes.get(abi) * 1048576 + defaultConfig.versionCode } } } } dependencies { compile project(':react-native-orientation') compile project(':react-native-video') compile project(':react-native-vector-icons') implementation fileTree(dir: "libs", include: ["*.jar"]) implementation "com.android.support:appcompat-v7:27.0.3" implementation "com.facebook.react:react-native:+" // From node_modules } // Run this once to be able to run the application with BUCK // puts all compile dependencies into folder libs for BUCK to use task copyDownloadableDepsToLibs(type: Copy) { from configurations.compile into 'libs' } 方法,我就编辑了答案以显示解决方案。

.ToString()