«

»

Print this Post

The power of Variants

This blogpost of Arend Jan Kauffman reminded me of a topic that I wanted to write a very long time now. Namely working with Variants. Sometimes I get the remark: “what the hell are they useful for? “. And to be honest: Variants could be bloody useful in some cases. Cases to make stuff even more generic then only working with RecordRef, FieldRef and such. The keyword is “Generic” here. Just trying not to create code that is copy-pasteable (if that is a word at all) .. but try that different parts of the application will run exactly the same code.

RecordRef

When you read the above .. you immediately think of RecordRef, RecordID, FieldRef, … all those kind of things. And indeed .. Variants can help you be even more generic .. in combination with RecordRef.

The case

I’m going to try to explain this with a very simple use case which came to speak on a confcall yesterday: Opening a Card page from any record. You know, sometimes, opening a card for a record depends on the data on which card page you would like to open. If you look at the Sales list page, you see this:


What if I want to make this code a little bit (let’s say “much more”) generic/repeatable? First of all: I don’t want to code like that on a page .. and second: I can imagine that other tables/data/pages has the same kind of “Open-Card-For-Certain-Records”-issues.. .

What if …

What if I could write something like this?


On any of the pages, for any record, any place, any time, any … (one?). So, exacly the same code on the “Card” action of the “Sales List” as on the “Card” action of the “Purchase List”. You would expect you need two functions, because you’re passing either the Sales Header of Purchase Header record.

How can Variants help me in this?

Let’s use the “DrillDown” method to explain what I did.. .

This is what I the result is in my two pages:


You can see that I have exactly the same function of the same codeunit, and passing a record. A different type of record. One belongs to Sales Header, the other to Purchase Header.

When you drill down, you see that Variants is helping me on this:


As you can see: I’m handling the Variants .. and even went a small stem further. With the code above, you’re even able to not only pass different tables, but you’re also able to use that same function to pass RecordRef. How Generic does one want to be?

When you drill down to the “OpenCardPage”-function, you arrive in my codeunit where I manage all this, for all records:


This way, it’s centrally managed, one place, copy-able functions, .. and most important: re-usable functions (not like the default one on the page itself).

Is this all?

Not necessarily. There is a lot you can do with Variants. It’s not my intention to show you everything (that’s not possible), just a few thouchpoints.. .

So may be another touchpoint then?

Well .. one of the PRS “Design Patterns” is “Facade” (or at least, that’s what we call it now – might change ;-)). Typically in a Facade pattern, you define “codeunits” as pieces of “apps” you’re going to execute. Like a codeunit for a certain scale to get the weight of something during a business process. Another Scale – another codeunit.

In some cases, the setup is going to define which record you’re going to “send” with your codeunit. For example, I have some kind of “Validation Framework” where I set up which codeunits have to run on which triggers and so on.. . Set up could look like this:


Codeunits, that has to run on certain tables .. .

When you’re calling your generic codeunit (which we call the “Facade”-codeunit), it’s typical you’re getting a RecordRef again .. and passing RecordRef to a codeunit is not possible.. .

So this is what you could do:


First: create a variant of your recref

Second: Pass that variant to the codeunit.

This is somewhat similar to Arend Jan’s blog (he’s using it with pages).

Conclusion

At first .. it’s difficult to imagine what this can do for you. But in terms of “repeatability”, “upgradeability” and “maintainability” .. it is sooo powerful :-). Hopefully you agree.

If you have any other examples, please share! :-).

4.43 avg. rating (89% score) - 7 votes

Permanent link to this article: http://www.waldo.be/2013/04/25/the-power-of-variants/

38 comments

2 pings

Skip to comment form

  1. Johannes Sebastian

    This is great, thanks for sharing with practical examples

  2. waldo

    y’re welcome 🙂

  3. Bogdana Botez

    Hey, nice to see your solution so soon after the talk yesterday, let me share this with my team.

  4. waldo

    Well, I actually prepared a whole different example .. but this one was more understandable, so I took that one.
    In our inhouse solution, we have elaborated it a little bit more .. making it possible for Lists as well .. and let you choose to run it MODAL or not.. . It’s just a few lines more.
    We also use it for all the frameworks that we have developer, that are linked with RECORDID to any other functionality in NAV. Something like the default RecordLinks. The big advantage is that you can have a list of your entire table (like the RecordLink table), and drill down to the exact page of that record where the linked record is all about.. . Actually just like the Notification-functionality (drilling down to the document where you added the notification) .. but then for our own stuff :-).

    Just an idea …

  5. deV.ch

    I use variant quite often when i need to do generic functions, the last thing i did was some sort of a try-get construct for our main solution. The problem was that we moved from a country specific implementation to a W1 implementation. Since not every field we use is available on W1 i needed a way to retrieve the field values when they are there but ignore it when not. Here is what i used:

    TryGetCode(_Record : Variant;_FieldName : Text) : Code[20]
    IF TryGetFieldRefByName(_Record, _FieldName, FRef) THEN
    IF FORMAT(FRef.TYPE) = ‘Code’ THEN
    EXIT(FRef.VALUE);

    Usage:
    “Location Code” := Framework.TryGetCode(Item, ‘Location Code’);

    I realy like the generic way, i hope you will come up with more of that stuff 🙂

    1. wakestar

      Hi there
      how does the code for ‘TryGetFieldRefByName’ look like?

    2. devch

      Hi wakestar, its just a simple FieldRef loop to get the field by using the field’s name, here the code:

      TryGetFieldRefByName(_Record : Variant;_FieldName : Text;VAR _FRef : FieldRef) : Boolean
      RecRef.GETTABLE(_Record);
      FOR i := 1 TO RecRef.FIELDCOUNT DO BEGIN
      _FRef := RecRef.FIELDINDEX(i);
      IF _FRef.NAME = _FieldName THEN
      EXIT(TRUE);
      END;

    3. waldo

      I was just working on an example myself .. but you saved me the time. Thanks, man!

      If you’re not blogging the example, I will (and give you the full credits ovbiously) 😉

    4. devch

      Hi Waldo, Feel free to use my example and blog about it. A credit with reference to my blog would be nice 😀
      Have a nice day!

    5. waldo

      Will do 😉
      Thanks!

    6. wakestar

      Thanks… that’s what I was expecting…

      … since there is no
      _FRef := RecRef.FIELDNAME(‘my fieldname’)
      unfortunately

  6. Rick Blom

    Smart programming is the highway to repeatability;-)

  7. waldo

    Thanks for that example, “deV.ch” 🙂

  8. wakestar

    hi waldo

    I looked at the codeunit example again…

    To me your table (screenshot) looks like you could setup any table to run with any codeunit.

    But if I understand correctly you always have to define the record type in the target codeunit (table no property) to be able to pass the record as a variant. Which means that every codeunit can only handle one table.
    If you don’t define table no… you cannot access the variant which you pass..
    Or am I missing something?

  9. Jan Hoek

    Hi Waldo,

    I noticed that your variant parameter in the code sample above is passed by reference (VAR). Are you sure that’s necessary?

    In my tests in NAV 2013, it seems records passed in as variants are *always* treated as “by value”, and recordrefs are *always* treated as “by reference”, regardless of whether the variant in question is VAR or not. As a consequence, the function’s behaviour strongly differs (in terms of active filters, temporary, active sort order), depending on whether you passed it a recordref or a record.

    In my book, that reduces not only the elegance of the solution, but also its usability…

    Would you agree?

    1. waldo

      Absolutely right.

      In fact .. the example above won’t even compile in NAV 2015! :-).

      The compiler will error out when you’re passing e.g. a recref variable to a byref variant parameter. Which makes sense. You’ll only be able to pass variants to ByReference variant-parameters .. .

      Good catch 😉

  10. Capone

    Unfortenately this doesn’t seem to work with pre 2013 installations unless I’m doing something wrong.
    I tried to test in 5.0 SP1 and 2009 R2 but it can’t cast a a record to variant in the function call.

    1. vittorio delmedico

      I used your code to implement Facade pattern and I receive the following error when I call the part
      PassedVariant.ISRECORD :
      RecRef.GETTABLE(PassedVariant);
      ‘Invalid table handle.The record parameter has not been properly initialized’
      Any susggestions why this doesn’t work (I use NAV 2009 R2)?

    2. waldo

      If I remember correctly, the Variant-thing doesn’t really work on NAV2009 .. . You might want to verify that, because it’s just an assumption based on a (terrible) memory 😉

    3. vittorio delmedico

      Thanks, based on the error I copied I would say definitely it doesn’t work on 2009R2, I needed to know if anybody had witnessed such error message and if tried to resolve it in any manner
      Thanks

  11. Erik F.

    I cant believe I only found this now. Thank you so much for this Info… I’ve been searching for ways to programm more flexible codeunits for a while.

    1. waldo

      Glad you like it 🙂

  12. Chrstian

    Have you made some experience with NAV2015 CU3? I get a compile error for type mismatch Variant := Record

    1. waldo

      I haven’t implemented CU3 yet.

      But one thing pops up in my mind: check if that specific Variant is a parameter that is set up By Reference. You shouldn’t assign a record/recordref to it, because the “byref” would fail. I know NAV2015 is giving an error on that now ..

    2. Chrstian

      Hm ok, we pass it By Value and seems not to work. I’ll going to test it with CU4. Thanks for your comment

    3. Chrstian

      Same behavior in NAV2015 CU4…

  13. jremaud

    Hi Waldo,

    I just read your post 2 years later… but it’s still actual for me!
    I am looking to your example, and I can’t figure out how/when is the variabler SalesHeader intantiate in the CU 51531, fonction OpenCardPage_SalesHeader(…)? I have to try it by myself…

    Thanks for all your post,
    Have a nice day,

    1. waldo

      Y’re welcome!
      We use it every day .. and still loving it! 🙂

    2. jremaud

      Hi,
      I just write and test your example.
      And for me, one line is missing to open the card on the right record, in the OpenCardPage_SalesHeader function :

      pRecRef.settable(SalesHeader);
      WITH SalesHeader DO BEGIN

      Do you agree? Or do I miss something?
      I don’t want to be “the student who surpasses the teacher”, and want to be sure of my understanding…

      Thanks for your time

  14. Heinrich Vermeulen

    Hi,
    We are moving a company that make rubber bales to NAV 2013R2 now. They have only a handful of items, but requires many variants.

    BUT they need to move the bales between variants without item journals, i.e. only as a warehouse movement.

    What do you think is the best way to achieve this?
    Thanks
    Heinrich

    1. waldo

      I think you have the wrong impression of this blogpost. This post is about the technical datatype “Variant”, not the functional “Item Variant” :-).
      Sorry, I can’t help you with this. I suggest you put your question on the MiBuSo forum…

  15. Justinas

    Hi, Trying this in NAV 2016 – gives compilation error, that function expects variant and I’m trying to pass a record to it..

    —————————
    Microsoft Dynamics NAV Development Environment
    —————————
    Type conversion is not possible because 1 of the operators contains an invalid type.

    Variant := Record
    —————————
    OK
    —————————

    Any ideas?

    1. waldo

      my guess is that you declared your Variant parameter By Reference. IN that case, you would only be able to pass variants. If the parameter is not by reference, you can pass records.. .

    2. Capone

      Have you turned of so the parameter is not marked as var (By reference)?

  16. Wm. Matthew Street

    How about a new Variant Function Like Ok := MyVariant.ISEQUAL(NewVariant);

    returns True with both NewVariant is the same value as MyVariant.

    1. waldo

      Not a bad idea at all.. although it’s comparing objects. And when is an object equal .. might be difficult to define that.. .

  17. Andre

    Hi Waldo,

    thank you very much for your nice tutorial.
    I am wondering if it is possible to call functions of a RecordRef-variable?
    For example : If two tables have the same functions, with same parameters, but different code, this might be interesting. Do you know if there is some field-ref alike possibility for functions?
    I was not able to find out anything useful, neither in NAV nor in the internet.

    Thanks Andre 😉

    1. waldo

      Well, that’s not possible, I’m afraid. It should be, I totally agree, but it isn’t .. :(.

  1. The power of Variants - Waldo's Blog - Microsoft Dynamics NAV

    […] Continue reading » […]

  2. The power of Variants | Pardaan.com

    […] Bron : Waldo’s Blog Lees meer… […]

Leave a Reply

%d bloggers like this: