A List of Field Names as a String Array - LINQ Exp

2019-09-02 14:18发布

Hello MVC and LINQ Experts,

I have a Model that looks like this:

 public class SomeClass : IValidatableObject
 {
    public string SomeString { get; set; }
    public string SomeString2 { get; set; }
    public int SomeInteger { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
       //... IF there is some error...THEN
       yield return new ValidationResult("Some Error Message.", GetFieldNames(() => new []{ this.SomeString }));
    }

 }

As you can see, I am calling GetFieldNames that takes an expression, and returns to you the expression members as a string array. According to a book I read recently, the way to link an error to a field is to pass it as a string as follows:

  yield return new ValidationResult("Some Error Message.", new []{ "SomeString" }));

But I wanted to be Strongly Typed, so here is the method that I wrote:

  public static string[] GetFieldNames(Expression<Func<object[]>> exp)
    {
        //Build a string that will in the end look like this: field1,field2,field3
        //Then we split(',') it into an array and return that string array. 
        string fieldnames = "";

        MemberExpression body = exp.Body as MemberExpression;

        if (body == null)
        {   
            NewArrayExpression ubody = (NewArrayExpression)exp.Body;
            foreach(MemberExpression exp2 in ubody.Expressions)
            {
                fieldnames += exp2.Member.Name + ",";
            }
            fieldnames = fieldnames.TrimEnd(',');

        }
        if(fieldnames.Length > 0)
            return fieldnames.Split(',');
        else
            return new string[]{};
    }

Current Usage:

GetFieldNames(() => new[] { this.SomeString , this.SomeString2 });

Output:

{ "SomeString" , "SomeString2" }

This works fine.

The problem is that if I use it as follows, it gives me an error (compile time):

GetFieldNames(() => new[] { this.SomeString , this.SomeInteger });

Error: No best type found for implicitly-typed array

My Desired Output:

{ "SomeString" , "SomeInteger" }

I can't pass in an array of object because int is not a complex type.

How can I pass the function an expression array with int and string?

3条回答
beautiful°
2楼-- · 2019-09-02 14:54

You could define an interface IFieldName that enables usage in your list, and then implement it in different classes (int, error, string, etc.) for the actual types that occur in your processing.

This is roughly equivalent to defining an rray of object, but restores type-safety.

查看更多
The star\"
3楼-- · 2019-09-02 15:05

You could try passing an array of objects (which is what your expression expects) instead of trying to use an array initializer syntax:

GetFieldNames(() => new object[] { this.SomeString, this.SomeInteger });

This allows you to pass arbitrary object types.

查看更多
姐就是有狂的资本
4楼-- · 2019-09-02 15:05

With the help of Darin Dimitri (the idea to pass a new object[] instead of new [] The following code will make sure that your IValidatableObject can now be strongly typed instead of just an array of strings.

    public static string[] GetFieldNames(Expression<Func<object[]>> exp)
    {
        string fieldnames = "";

        MemberExpression body = exp.Body as MemberExpression;

        if (body == null)
        {
            NewArrayExpression ubody = (NewArrayExpression)exp.Body;
            foreach (Expression exp2 in ubody.Expressions)
            {
                if (exp2 is MemberExpression) {
                    fieldnames += ((MemberExpression)exp2).Member.Name + ",";
                }
                else {
                    var op = ((UnaryExpression)exp2).Operand;
                    fieldnames += ((MemberExpression)op).Member.Name + ",";
                } 
            }
            fieldnames = fieldnames.TrimEnd(',');

        }

        if(fieldnames.Length > 0)
            return fieldnames.Split(',');
        else
            return new string[]{};
    }

Usage:

GetFieldNames(() => new object[] { this.SomeString, this.SomeInteger }));

Usage for MVC Validation:

yield return new ValidationResult("Some Error.", GetFieldNames(() => new object[] { this.SomeString, this.SomeInteger }));

查看更多
登录 后发表回答