2

This question is a continuation of this question here. If someone cares to know why I need to do things like this, you can find the rationale in that question. Not that it matters, really.

I need a method like this:

public virtual Expression<Func<T, object>> UpdateCriterion()
{
    // this doesn't work because the compiler doesn't know if T has Id & CompanyId
    return e => new { e.Id, e.CompanyId }; 
}

The problem is, there is no supertype for T that I could use to pull out Id and CompanyId from, I have to do it dynamically. Thanks to the answer to that referenced question, I have successfully built and used this kind of method for one property (e => e.Id), but I'm having issues implementing it for two. Just for visibility, the solution for one field is:

public virtual Expression<Func<T, object>> UpdateCriterion()
{
    var param = Expression.Parameter(typeof(T));
    var body = Expression.Convert(Expression.Property(param, "ID"), typeof(object));

    return Expression.Lambda<Func<T, object>>(body, param);
}

I've been going nuts with this for over 6 hours... So, how do I solve this?

Community
  • 1
  • 1
Davor
  • 1,387
  • 16
  • 33
  • 3
    Do you have any control on those `T` subclasses? Setting an interface would solve the problem in most elegant way. – J0HN Jul 08 '14 at 11:55
  • I've explained in detail in that other question, but in short, no, that's not gonna work, because not all of them have those fields, so some classes override it, some don't. I need this as base implementation. – Davor Jul 08 '14 at 11:57
  • 1
    Is that really your working code? It won't compile for me... – Anders Abel Jul 08 '14 at 11:57
  • It is. What error do you get? – Davor Jul 08 '14 at 11:58
  • .NET 4.5.1. You can see context in the linked question if you're interested. – Davor Jul 08 '14 at 12:01
  • 1
    If not of them have the same set of fields than you probably just need a couple of interfaces (e.g. `ISomethingWithId`, `ISomethingWithIdAndCompanyId` etc.). In my opinion reflection is pretty expensive solution for virtually everything in terms of code complexity. – J0HN Jul 08 '14 at 12:03
  • I would argue on why would you want to have generic method for classes with totally different signatures. If they don't have much in common, why would you want to treat them like if they had? Technically it's an interesting question, but it really looks like it's just designed wrong way. – Tarec Jul 08 '14 at 12:03
  • @Davor: I used linqpad targeting .NET 3.5 for testing, that's why it failed. With .NET 4.5 it works. – Anders Abel Jul 08 '14 at 12:13
  • Just one more question: how many entity classes do you have? – J0HN Jul 08 '14 at 12:17
  • Well, I'm not lecturing you, but, as you said, you have spent 6 hours trying to come up with really complex and hard to mantain solution for a problem that could be solved by manually extracting a couple of interfaces and rewriting single class to operate on those interfaces rather than some abstract entity. As you add more and more entites you'll either end up having same "signature" or completely new "signature". For same signature you'll only have to add an interface to an entity (1 line of code), for new "signature" - 1 interface and one method to work on it. So it's really simple – J0HN Jul 08 '14 at 12:25
  • On the other had, with such overgeneralised solution as you are trying to build, each time you add a new "signature", you'll have to solve this problem again, trying not to break old entites in process. So, I would definitely think again, if you really need this expression-reflection-generics approach. – J0HN Jul 08 '14 at 12:27
  • I believe I'm right in saying that you might a well make an `IdAndCompanyId` class and create an `Expression>`, because that's essentially what the compiler would give you if you used an anonymous class. (Even though it's going to EF. I think.) – Rawling Jul 08 '14 at 12:33
  • @J0HN - When you add new entity with new signature, it simply needs to override the `UpdateCriterion` with it's own signature, say, `UpdateCriterion() { return e => e.Name; }`. I'm not sure why you think this is complicated, the entire solution is one method in base class and a few overrides in subclasses which don't adhere to the stadard. – Davor Jul 08 '14 at 12:35
  • And you'll end up exactly where you are now. What if a significant number of classes share exactly the same `UpdateCriterion` override? – J0HN Jul 08 '14 at 12:41
  • @J0HN - I'll have that implementation in the superclass, overridable? That's the whole thing I'm solving here? – Davor Jul 08 '14 at 12:43
  • I mean, what if you have at least two significant groups of entities with same signatures inside the group. – J0HN Jul 08 '14 at 12:45
  • I wont. If I do, I'll refactor it to something else, who knows? – Davor Jul 08 '14 at 12:48
  • Ok, all in all that's your project and you'll have to mantain it. I believe I'm not smart enough to code up what you are trying to achieve in some sensible time (read: until it's not related to my own job) :). But I hope I gave you at least an idea on how to approach it from another angle. Good luck. – J0HN Jul 08 '14 at 12:55
  • @J0HN - you did, and I had the same ideas, trust me. But I think it will take me more to talk people into inheriting interfaces and make other changes to their ancient code then to just extend it with reflection and dynamics... – Davor Jul 08 '14 at 13:36

2 Answers2

1

The Body of this Lamba is a MemerInitExpression.

That was the easy part. The bigger Problem here is that you use an Anonymous Type in your Lambda.

Expression<Func<TranslatedText, object>> exp;
exp = p => new { p.LanguageId, p.TextId};

If you use such an AnonymousType, the Compiler will inspect your code, detect the AnonymousType declaration and will create a Type like this on the fly.

public class f__AnonymousType0
{
    public int LanguageId { get; set; }
    public int TextId { get; set; }
}

And change your lambda into something like this.

exp = p => new f__AnonymousType0 { LanguageId = p.LanguageId, TextId = p.TextId };

Because you would like to create the lambda at runtime the f__AnonymousType0 type you need for the MemberInitExpression does not exist.

As you need an actual Type to create this Expression you have two options to get one.

1 - Write some generic classes like the Tuple class of the .NET Framework. Of cores this solution is limited to a maximum amount of properties.

pro: easy to create and use – con: limited property count.

public class KeyTuple<T1, T2>
{
    public T1 Item1 { get; set; }
    public T2 Item2 { get; set; }
}

public class KeyTuple<T1, T2, T3>
{
    public T1 Item1 { get; set; }
    public T2 Item2 { get; set; }
    public T3 Item3 { get; set; }
}

public class KeyTuple<T1, T2, T3>
public class KeyTuple<T1, T2, T3, T4>
public class KeyTuple<T1, T2, T3, T4, T5>
public class KeyTuple<T1, T2, T3, T4, T5, T6>

2 - You could use Reflection.Emit and create a type at runtime http://www.codeproject.com/Articles/121568/Dynamic-Type-Using-Reflection-Emit

pro: unlimited property count – con: complicated

When you have a type you can use the Expression tree api you already know to crate the lambda

var keys = new[] { "LanguageId", "TextId" };

var param = Expression.Parameter(typeof(TranslatedText));
var properties = keys.Select(p => Expression.Property(param, p)).ToList();

var keyTupleType = typeof(KeyTuple<,>).Assembly.GetType(string.Format("AnonymousTypeExpression.KeyTuple`{0}",keys.Count()));
keyTupleType = keyTupleType.MakeGenericType(properties.Select(p => p.Type).ToArray());

var bindings = properties.Select((p,i) => Expression.Bind(keyTupleType.GetProperty(string.Format("Item{0}",i + 1)),p)).ToArray();
var body = Expression.MemberInit(Expression.New(keyTupleType), bindings);
var result= Expression.Lambda<Func<TranslatedText, object>>(body, param);

This creates an expression that looks like this

exp = p => new KeyTuple<int, int> { Item1 = p.LanguageId, Item2 = p.TextId };
codeworx
  • 2,715
  • 13
  • 12
0

Try using reflection ?

Please not i don't have visual studio or any other IDE in front of me so the code my contain some bugs and typos

Also notes if you don't need properties, you may need fields there are also GetField() and GetFields() look them around.

public virtual Expression<Func<T, object>> UpdateCriterion()
{

    return e => new { GetPropertyValue<T>(e,"id"), GetPropertyValue<T>(e,"CompanyId") }; 
}

public object GetPropertyValue<T>(T TargetObject,string PropertyName)
{
     var prop = typeof(T).GetProperty(PropertyName).GetValue(TargetObject, null);
}
sm_
  • 2,572
  • 2
  • 17
  • 34
  • If, as Davor said, not all classes can be guaranteed to have implementations of both properties then I believe this is the way to do it. – JoelC Jul 08 '14 at 12:21
  • 2
    From the context, it looks like this is being used with Entity Framework. I don't think this is going to translate. – Rawling Jul 08 '14 at 12:24
  • This would not work, if he needs the expression tree itself and not the values – sm_ Jul 08 '14 at 12:24