Using .NET Predicate (or Constraints on Type) in NAV

The other week I attended the UK PowerShot Workshop hosted at Microsoft HQ in Reading and run by the guys from Cloud Ready Software (Dynamics NAV’s very own Gang of 4: WaldoGaryVjeko and Mark Brummel – Mark wasn’t there though). Waldo has posted about where it was and what was covered – suffice to say, it was great and I learnt a lot of stuff (and even more stuff that I put on my “need-to-play-with” list)… This blog post is not going to cover all that.

What this blog is about is a tiny thing that Waldo brought up where a .NET interop feature seems to be impossible to use in C/AL. You can read about the scenario and the problem (search for “predicate”) here. I was confused about this as I didn’t know how a predicate would stop NAV compiling.

So what is a “predicate”? A predicate is basically a function which takes a specific type that returns a boolean (http://www.dotnetperls.com/predicate) but in .NET you normally are talking about predicate delegates which are just pointers to that function (https://msdn.microsoft.com/en-us/library/bfcke1bz(v=vs.110)).

This didn’t really help me to understand.

Let’s dive into the object, in this case PSObject and the property in question has the type PSMemberInfoCollection which has the following Syntax. The words “where T” give the answer of how predicates are involved, this means that this collection has a Constraint on it’s Type (see MSDN reference).

C#
[DefaultMemberAttribute("Item")] 
public abstract class PSMemberInfoCollection<T> : IEnumerable<T>, IEnumerable where T : PSMemberInfo

Why does this complain when you try to compile it in NAV? This is what you get when you try and reference that property.

.NET interop: exception occurred, 'GenericArguments[0], 'System.Object', on 'System.Management.Automation.PSMemberInfoCollection`1[T]' violates the constraint of type 'T'.'.Could not load type 'System.Management.Automation.PSMemberInfoCollection`1.'System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35''.

The first error tells you what is going wrong. At compile time NAV is trying to use System.Object where .NET has a constraint for <T>. Ignore the left-hand side of that C/AL line by the way – it doesn’t matter what you are assigning to (this is a whole other interesting casting issue which will have to be another blog post…), or even if you aren’t assigning it to anything, you just cannot type PSObject.Properties in C/AL or the compiler will complain.

Why is it doing that? I don’t know. Look at Vjeko’s comments on using generics (i.e. anything .NET with <T> in its definition) and what is still missing from .NET Interop and you might start to get some ideas…

The thing is, we know that the code will actually run in the .NET runtime so should work – it’s just the compile that is complaining. So we need to get to the property without letting the compiler know that’s what we are doing. How do we do that? Reflection.

C/AL
Type := GETDOTNETTYPE(PSObject);
IEnumerable := Type.GetProperty('Properties').GetValue(PSObject);

Note that I am using IEnumerable on the left hand side as NAV will still complain if I try to use the real type of PSMemberInfoCollection (again you need to be careful with this “cast” as it doesn’t necessarily work how you expect it to – next blog…).

Then I spent quite a lot of time trying to use the .ToArray() function to fit in with Waldo’s solution but it’s a “extension method” (#3 in Vjeko’s missing list) from LINQ so I couldn’t get it to work. Eventually I went back to basics and just looped through it (PSPropertyInfo[n] is a DotNet variable with 9999 Dimensions in C/SIDE – basically it’s a buffer for the eventual DotNet array, which is basically what you get if you look behind the scenes of how .ToArray() works)

C/AL
n := 0;
IEnumerator := IEnumerable.GetEnumerator();
WHILE (IEnumerator.MoveNext()) DO BEGIN
 n += 1;
 PSPropertyInfo[n] := IEnumerator.Current();
END;
CLEAR(IEnumerator);

PsPropertyInfoArray := PsPropertyInfoArray.CreateInstance(GETDOTNETTYPE(PSPropertyInfo[1]),n);
FOR i := 1 TO n DO BEGIN
 PsPropertyInfoArray.SetValue(PSPropertyInfo[i], i - 1);
END;

This seems to work well. 🙂

I’m not saying it should replace Waldo’s trick with creating a wrapper at runtime (I love that trick!!) but it’s another option.

Nikolai

Advertisements

6 thoughts on “Using .NET Predicate (or Constraints on Type) in NAV

  1. Hi Nikolai. Thank you for mentioning me, however, I am not part of the Cloud Ready Software company.

    My guess is that you confuse this company with the Partner Ready Software Community initiative. This also includes Waldo, Gary and Vjeko, plus me and Soren Klemmensen.

    CRS is a company with commercial interests where PRS is not for profit. My decision was to only be part of te latter.

    I apologise for the confusion. I voted against the ambiguous naming. 😉

    Like

  2. Hi Nikolai,

    I see this post only now – I’ll have to correct you, sorry 🙂 What you are using here really has nothing to do with predicates. Predicates do involve generics, and the way how NAV treats generics at runtime is at the heart of the problem, bot the one you solved here, and the one Waldo talked about in his blog. You can trick NAV to handle generics properly (you pointed here to my blog where I explain how) but you can’t trick it to handle delegates. Just clearing confusion, because people might think this solves the predicate problem, while it doesn’t. It only solves the generic type incompatibility issue (because System.Object cannot be cast to PSMemberInfo).

    Sorry for being a smartass, just thought about helping others looking for solution for predicates in C/AL.

    Cheers,

    Vjeko

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s