我有一个带有某些属性的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.Call
和Expression.Property
的人进行了讨论,但不幸的是,我没有足够的知识来正确使用它。
在这里,我没有太多细节就公开了我的问题,但是我有理由仅以此方式创建方法。
谢谢。
答案 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()