4

After spending a year working with the Microsoft.Xrm.Sdk namespace, I just discovered yesterday the Entity.FormattedValues property contains the text value for Entity specific (ie Local) Option Set texts.

The reason I didn't discover it before, is there is no early bound method of getting the value. i.e. entity.new_myOptionSet is of type OptionSetValue which only contains the int value. You have to call entity.FormattedValues["new_myoptionset"] to get the string text value of the OptionSetValue.

Therefore, I'd like to get the crmsrvcutil to auto-generate a text property for local option sets. i.e. Along with Entity.new_myOptionSet being generated as it currently does, Entity.new_myOptionSetText would be generated as well.

I've looked into the Microsoft.Crm.Services.Utility.ICodeGenerationService, but that looks like it is mostly for specifying what CodeGenerationType something should be...

Is there a way supported way using CrmServiceUtil to add these properties, or am I better off writing a custom app that I can run that can generate these properties as a partial class to the auto-generated ones?

Edit - Example of the code that I would like to be generated

Currently, whenever I need to access the text value of a OptionSetValue, I use this code:

var textValue = OptionSetCache.GetText(service, entity, e => e.New_MyOptionSet);

The option set cache will use the entity.LogicalName, and the property expression to determine the name of the option set that I'm asking for. It will then query the SDK using the RetrieveAttriubteRequest, to get a list of the option set int and text values, which it then caches so it doesn't have to hit CRM again. It then looks up the int value of the New_MyOptionSet of the entity and cross references it with the cached list, to get the text value of the OptionSet.

Instead of doing all of that, I can just do this (assuming that the entity has been retrieved from the server, and not just populated client side):

var textValue = entity.FormattedValues["new_myoptionset"];

but the "new_myoptionset" is no longer early bound. I would like the early bound entity classes that gets generated to also generate an extra "Text" property for OptionSetValue properties that calls the above line, so my entity would have this added to it:

public string New_MyOptionSetText {
    return this.GetFormattedAttributeValue("new_myoptionset"); // this is a protected method on the Entity class itself...
}
Matt
  • 4,656
  • 1
  • 22
  • 32
Daryl
  • 18,592
  • 9
  • 78
  • 145
  • Can't you just call `.ToString()` on the enum value (e.g. `SalesOrderState.Invoiced.ToString()`), or are you talking about something else? – Peter Majeed Nov 01 '12 at 17:26
  • @PeterMajeed Calling ToString on the Option Set generated enum doesn't work all the time (what if the text value was "Ready to Ship", then enum.ToString() would be "ReadytoShip". Another one that wouldn't work is say "A+"). I've currently written code for an interface for the CrmServiceUtil that ensures that all optionset text values are valid C# enum names, so I know that method won't work. – Daryl Nov 01 '12 at 17:37
  • I think it's an excellent question given the lack of documentation for CrmSvcUtil. Still, I don't see why you'd need an early bound version of the string value of an option set when you can access the string value via a formatted value. Do you think you can share a few lines of code/pseudocode that demonstrates your end purpose? – Peter Majeed Nov 02 '12 at 16:29

3 Answers3

0

Could you utilize the CrmServiceUtil extension that will generate enums for your OptionSets and then add your new_myOptionSetText property to a partial class that compares the int value to the enums and returns the enum string

Chris Snyder
  • 958
  • 4
  • 10
  • I should have read your comment more carefully...looks like you already considered this. – Chris Snyder Nov 02 '12 at 13:11
  • Actually with the formatted values, I don't have to compare the int value to anything. It actually contains the text value of the option set value. What the question is asking is, is there a way to override property creation using an interface with crmsrvcutil, that allows me to add an additional early bound property for the option set value text... – Daryl Nov 02 '12 at 13:15
  • Good question! That would be a nice extension point if you are able to do that. – Chris Snyder Nov 02 '12 at 13:22
  • I'm thinking I'm going to have to write a console app that runs against the generated code, determines if each option set is global or local, then generates partial classes with the local option set value text properties. It shouldn't be too hard... – Daryl Nov 02 '12 at 13:24
  • Using reflection or RegEx (text)? Would the MetaDataService have that info? With my MSFT Currency issue, I was reintroduced to the Attributes table...SQL might be a quick/dirty plan B – Chris Snyder Nov 02 '12 at 13:28
  • The Reflection to get the properties that are OptionSetValues is easy. We usually don't give Database rights out to users, so I'll probably stick to a couple SDK calls... – Daryl Nov 02 '12 at 13:31
0

Again, I think specifically for this case, getting CrmSvcUtil.exe to generate the code you want is a great idea, but more generally, you can access the property name via reflection using an approach similar to the accepted answer @ workarounds for nameof() operator in C#: typesafe databinding.

var textValue = entity.FormattedValues["new_myoptionset"];
// becomes
var textValue = entity.FormattedValues
[
    // renamed the class from Nameof to NameOf
    NameOf(Xrm.MyEntity).Property(x => x.new_MyOptionSet).ToLower()
];
Community
  • 1
  • 1
Peter Majeed
  • 5,304
  • 2
  • 32
  • 57
  • The code for the properties should be autogenerated, which means that having a "hardcoded" string is totally acceptable. Also, not every option set value property would have a corresponding text property, because not every option set is a local one. Therefore, even though this would be an "earlybound" sort of method of getting the property, it doesn't help at all in aiding developers in seeing what properties are truly available to them. – Daryl Nov 05 '12 at 13:19
  • @Daryl: Not sure if the distinction between global and local option sets is a significant one, since ultimately all global option sets are stored locally and can be accessed similarly using the `FormattedValues` property. So far as seeing which properties are really available, if I understand correctly, this is a problem that can be solved I believe by cross-checking the list of entity `Attributes` against reflected `Xrm` properties, which would need to be checked when using the `FormattedValues` array anyway. – Peter Majeed Nov 05 '12 at 15:34
0

The latest version of the CRM Early Bound Generator includes a Fields struct that that contains the field names. This allows accessing the FormattedValues to be as simple as this:

var textValue = entity.FormattedValues[MyEntity.Fields.new_MyOptionSet];

You could create a new property via an interface for the CrmSvcUtil, but that's a lot of work for a fairly simple call, and I don't think it justifies creating additional properties.

Daryl
  • 18,592
  • 9
  • 78
  • 145