seanmcne / Microsoft.Xrm.Data.PowerShell

This module uses the CRM connection from Microsoft.Xrm.Tooling.CrmConnector.Powershell and provides common functions to create, delete, query, and update data as well as functions for common tasks such as publishing, and manipulating System & CRM User Settings, etc. The module should function for both Dynamics CRM Online and On-Premise environment.
201 stars 64 forks source link

-IncludeNullValue for Get-CrmRecords as well #523

Open raph91 opened 1 year ago

raph91 commented 1 year ago

Hello

Re: https://github.com/seanmcne/Microsoft.Xrm.Data.PowerShell/issues/224

I thought I create a new issue as the comment could be overlooked.

The same issue occurs with Get-CrmRecords. Is there a similar parameter? I am able to get the field itself with Get-CrmRecord and the parameter "IncludeNullValue", but when I want to get it for multiple accounts (eg. for all accounts from a specific location), it does not work. As a workaround, I have to iterate through all accounts and get the record again, so I read data twice which is not very efficient...

Maybe it would make sense to add the parameter to Get-CrmRecords as well.

BR

seanmcne commented 1 year ago

@raph91, I'd have to take a look into this and unfortunately, I've been a little short on time lately. Happy to review if you have an example for how you'd implement it (either via a PR or here would be fine). Off hand, the way the dataverse platform works is if a value of an attribute for a record is null, the API response leaves it out so there needs to be some assumptions made and/or metadata checks.

We could potentially re-use what Ken did in 224, but there is a performance cost/overhead and I'd have to look into how he solved it for individual records and see if that would easily translate to collections, or not (the performance overhead might be pretty big). I've run into this issue once or twice over the years and essentially do the same thing where I added my own default field/attribute in the object with a null value or added a check in my code to check the object for the value and assume it's null if it's not there. The upside with leaving this up to the developer is that the proper assumptions can be made and tested and hopefully avoid errors but I can see where there are some cases where this would be helpful.

raph91 commented 1 year ago

Hi @seanmcne

Thank you for your response! Good idea with adding the missing properties manually. This is a lot faster than getting the records twice and works like a charm. In my case, performance is not that important for now as the script will run in the background at a specific time. Even when: If a developer wants to fetch ALL data, a certain impact on performance must be expected in my opinion.

To be more specific: 5087 records need 13 more seconds now to add the missing properties, 13 manageable seconds.

If someone is looking for a similar solution, I was able to solve it with the following snippet:

foreach ($Record in $Records.CrmRecords) {
    foreach ($Field in $Fields) {
        if (-not $Record.PSObject.Properties.Name.Contains($Field)) {
            $Record | Add-Member -NotePropertyName $Field -NotePropertyValue $null
        }
        if (-not $Record.PSObject.Properties.Name.Contains("$($Field)_Property")) {
            $Record | Add-Member -NotePropertyName "$($Field)_Property" -NotePropertyValue $null
        }
    }
}

EDIT: Well, I celebrated too early, unfortunately it does not work as desired... It works for strings, but when I want to update an option set (eg. industrycode) field that is empty in Dynamics 365, I get the following error:

Incorrect attribute value type System.Int32 => Incorrect attribute value type System.Int32 => System.ArgumentException: Incorrect attribute value type System.Int32 Parameter name: industrycode[TerminalFailure] Failed to Execute Command - Update : RequestID=efd7df81-549b-4cb8-bfc2-0e141fc33a96 : Updating account : 9620cf26-b091-ed11-b818-00155d0105da duration=00:00:01.6755919 ExceptionMessage = Incorrect attribute value type System.Int32 Incorrect attribute value type System.Int32 => Incorrect attribute value type System.Int32 => System.ArgumentException: Incorrect attribute value type System.Int32 Parameter name: industrycode

Still looking for a solution...

EDIT 2: New-CrmOptionSetValue is the savior. Now it seems to work.