CRM SDK OrganizationServiceContext null navigation properties on entity - dynamics-crm

Trying to migrate an existing solution away from the deprecated Microsoft.Xrm.Client namespace to just use the generated service context from CrmSvcUtil using CrmSDK 9.0.0.5.
Previously we were using Microsoft.Xrm.Client.CodeGeneration.CodeCustomization to get a lazily loaded context.
I have two copies of the same solution and have been working through some of the API changes.
I have enabled Proxy Types
client.OrganizationServiceProxy.EnableProxyTypes();
Which to my understanding switched it to act in a lazily-loaded manner. However, none of the navigation properties are loading as expected.
The few blog posts that I've found around this shift to CrmServiceClient etc suggest that even without lazy loading I should be able to load the property manually with a call to Entity.LoadProperty() which will either load the property or refresh the data. However, after doing that the navigation property is still null (specifically I'm trying to use a Contact navigation property). When I look through the RelatedEntities collection it is also empty.
I know that the entity has a related contact item as if I use a context generated with Microsoft.Xrm.Client.CodeGeneration.CodeCustomization it returns it and I can also see it in CRM itself using an advanced search.
var connectionUri = ConfigurationManager.ConnectionStrings["Xrm"].ConnectionString;
var client = new CrmServiceClient(connectionUri);
client.OrganizationServiceProxy.EnableProxyTypes();
var context = new XrmServiceContext(client);
var result = context.new_applicationSet.FirstOrDefault(x => x.new_applicantid.Id == CurrentUserId);
//result.Contact is null
context.LoadProperty(result, "Contact");
//result.Contact is still null
//result.RelatedEntities is empty

Related

Retrieve Plugin not getting triggered

We are on Dynamics CRM 2016 On-Premise. Using a plugin I'm trying to automatically update a field when a user open the CRM Account form, in this example to value "5". Here's my code:
var targetEntity = (Entity)context.OutputParameters["BusinessEntity"];
if (targetEntity == null)
throw new InvalidPluginExecutionException(OperationStatus.Failed, "Target Entity cannot be null");
var serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
var service = serviceFactory.CreateOrganizationService(context.UserId);
if (targetEntity.Attributes.Contains("MyField"))
fedTaxId = targetEntity.Attributes["MyField"].ToString();
targetEntity.Attributes.Add("MyField"d, "5");
targetEntity["MyField"] = "5";
service.Update(targetEntity);
I list this in message type 10 (Before Main Operation Outside Transaction).
In Plugin Registration I list this as Post Operation stage and Synchronous.
However when I open the Account form, the page blinks once but the value did not get automatically populated. There is no javascript that would've manipulated this form or value either.
Any suggestion? Thanks.
Two options:
Add a script on the form setting the field value on load. Keep in mind this script should only do its thing if the form type = 2.
(Not recommended) Register a plugin on the synchronous post retrieve message for the entity. Make sure this step sets the field value on the entity object in the OutputParameters collection. Now, keep in mind your form will not be aware of the fact that this field has been modified, so it will not be flagged dirty and it will not automatically be submitted when record changes are being saved. So, in this scenario you would still need to add some JavaScript OR you would need an extra plugin registered on the pre update message of the entity setting the field as desired.

MenuItemDefinition Order

I'm getting started with ASP.NET Boilerplate where I am using the free start-up template of ASP.NET Core Multi Page Web Application.
My question is regarding the left hand navigation bar. I have the NavigationProvider in my project where the abstract class is implemented. I have been testing out some of the properties available in the MenuItemDefinition, specifically the order and isVisible. My expectation was that I could set both the order and isVisible properties and that this would show in the application when running. I don't however see any of these properties actually being used to either set the order of the menu items or control their visibility.
For the order property I updated the Default.cshtml of the SideBarNav component to implement an OrderBy so I can get the correct order. This works but I'm not sure if this is the recommended approach.
Is it correct that setting the order when adding a MenuItemDefinition doesn't actually have any consequence to the running application and that this property is here with the expectation that I implement the behavior as I've done in the Default.cshtml?
My reason for wanting to control the order this way rather than by just the order the items are added is related to creating different modules that each have their own NavigationProviders.
Is it correct that setting the order when adding a MenuItemDefinition doesn't actually have any consequence to the running application and that this property is here with the expectation that I implement the behavior as I've done in the Default.cshtml?
Yes, the templates (as of v5.2.0) do not use nor implement ordering of user menu items.
I suggest implementing an extension method to easily change the ordering across the project.
public static IOrderedEnumerable<UserMenuItem> OrderByCustom(this IEnumerable<UserMenuItem> menuItems)
{
return menuItems
.OrderBy(menuItem => menuItem.Order)
.ThenBy(menuItem => menuItem.DisplayName);
}
Regarding versions: You are using v5.1.x and below. As of v5.2.0, the template has been migrated to use AdminLTE 3 instead of AdminBSB for the MVC UI, so the following changes are incompatible.
v5.2+
I have submitted a PR to implement the ordering: aspnetboilerplate/module-zero-core-template#510
var orderedMenuItems = Model.MainMenu.Items.Where(x => x.IsVisible).OrderByCustom().ToList();
var subMenus = Model.Items.Where(x => x.IsVisible).OrderByCustom().ToList();
Files changed:
Web.Mvc/Views/Shared/Components/SideBarMenu/Default.cshtml
Web.Mvc/Views/Shared/Components/SideBarMenu/_MenuItem.cshtml
v5.1.x and below
Note that you should change in 3 places (corresponding to 3 nesting levels) in Default.cshtml.
#foreach (var menuItem in Model.MainMenu.Items.OrderByCustom().ToList())
#foreach (var subMenuItem in menuItem.Items.OrderByCustom().ToList())
#foreach (var subSubMenuItem in subMenuItem.Items.OrderByCustom().ToList())
File changed:
Web.Mvc/Views/Shared/Components/SideBarNav/Default.cshtml

How to set cluster resource "Use Network Name for computer name" checkbox programmatically?

I am programmatically setting up a cluster resource (specifically, a Generic Service), using the Windows MI API (Microsoft.Management.Infrastructure).
I can add the service resource just fine. However, my service requires the "Use Network Name for computer name" checkbox to be checked (this is available in the Cluster Manager UI by looking at the Properties for the resource).
I can't figure out how to set this using the MI API. I have searched MSDN and multiple other resources for this without luck. Does anybody know if this is possible? Scripting with Powershell would be fine as well.
I was able to figure this out, after a lot of trial and error, and the discovery of an API bug along the way.
It turns out cluster resource objects have a property called PrivateProperties, which is basically a property bag. Inside, there's a property called UseNetworkName, which corresponds to the checkbox in the UI (and also, the ServiceName property, which is also required for things to work).
The 'wbemtest' tool was invaluable in finding this out. Once you open the resource instance in it, you have to double-click the PrivateProperties property to bring up a dialog which has a "View Embedded" button, which is then what shows you the properties inside. Somehow I had missed this before.
Now, setting this property was yet another pain. Due to what looks like a bug in the API, retrieving the resource instance with CimSession.GetInstance() does not populate property values. This misled me into thinking I had to add the PrivateProperties property and its inner properties myself, which only resulted in lots of cryptic errors.
I finally stumbled upon this old MSDN post about it, where I realized the property is dynamic and automatically set by WMI. So, in the end, all you have to do is know how to get the property bag using CimSession.QueryInstances(), so you can then set the inner properties like any other property.
This is what the whole thing looks like (I ommitted the code for adding the resource):
using (var session = CimSession.Create("YOUR_CLUSTER", new DComSessionOptions()))
{
// This query finds the newly created resource and fills in the
// private props we'll change. We have to do a manual WQL query
// because CimSession.GetInstance doesn't populate prop values.
var query =
"SELECT PrivateProperties FROM MSCluster_Resource WHERE Id=\"{YOUR-RES-GUID}\"";
// Lookup the resource. For some reason QueryInstances does not like
// the namespace in the regular form - it must be exactly like this
// for the call to work!
var res = session.QueryInstances(#"root/mscluster", "WQL", query).First();
// Add net name dependency so setting UseNetworkName works.
session.InvokeMethod(
res,
"AddDependency",
new CimMethodParametersCollection
{
CimMethodParameter.Create(
"Resource", "YOUR_NET_NAME_HERE", CimFlags.Parameter)
});
// Get private prop bag and set our props.
var privProps =
(CimInstance)res.CimInstanceProperties["PrivateProperties"].Value;
privProps.CimInstanceProperties["ServiceName"].Value = "YOUR_SVC_HERE";
privProps.CimInstanceProperties["UseNetworkName"].Value = 1;
// Persist the changes.
session.ModifyInstance(#"\root\mscluster", res);
}
Note how the quirks in the API make things more complicated than they should be: QueryInstances expects the namespace in a special way, and also, if you don't add the network name dependency first, setting private properties fails silently.
Finally, I also figured out how to set this through PowerShell. You have to use the Set-ClusterParameter command, see this other answer for the full info.

Adding DynamicPropertyInstance doesn't mark the SalesOrderDetail as valid

I have the following code:
var propertyInstance = new DynamicPropertyInstance()
{
DynamicPropertyId = new EntityReference(DynamicProperty.EntityLogicalName, Guid.Parse("0ceedfcc-68b2-e711-8168-e0071b658ea1")),
ValueString = jobId.ToString(),
RegardingObjectId = line.ToEntityReference(),
};
crmContext.AddObject(dynamicPropertyInstance);
crmContext.SaveChanges();
It is successfully adding a DynamicPropertyInstance to a SalesOrderLine, but when viewing the Order in the CRM UI it does not pass the validation (as it is a required property). I've not managed to find a way to make this property valid. Editing the property that I've added in the UI (resetting the value) also fails to mark the instance as valid. Adding exactly the same property through the UI does mark it as valid.
The Id of the DynamicProperty is correct, as verified by loading the 2 instance records through the SDK and comparing the properties. Rather strangely, when I load the 2 records through the SDK the one I've created in code has a validationstatus of true (even though it's not) and the one that I've created in the UI has a validationstatus of false and ValueString returns null (which is wrong). All of the other properties either match or have relevant values (such as dates, object Ids etc)
I'm probably missing a method call to recalculate whether the instance is valid or not, but I can't find anything in the documentation to support that. Failing that, it's possibly a bug in CRM
Raised a case with Microsoft support, and was given some workaround code:
//Get DynamicPropertyInstance
UpdateProductPropertiesRequest UpdateRequest = new UpdateProductPropertiesRequest();
UpdateRequest.PropertyInstanceList = new EntityCollection();
UpdateRequest.PropertyInstanceList.EntityName = DynamicPropertyInstance.EntityLogicalName;
Entity dpInstance = new Entity(DynamicPropertyInstance.EntityLogicalName, Dpi.Id);
dpInstance.Attributes.Add(nameof(Dpi.ValueString).ToLower(), "Blarg");
dpInstance.Attributes.Add(nameof(Dpi.DynamicPropertyInstanceid).ToLower(), Dpi.Id);
dpInstance.Attributes.Add(nameof(Dpi.RegardingObjectId).ToLower(), new EntityReference(SalesOrderDetail.EntityLogicalName, line.Id));
dpInstance.Attributes.Add(nameof(Dpi.DynamicPropertyId).ToLower(), new EntityReference(DynamicProperty.EntityLogicalName, dpId));
UpdateRequest.PropertyInstanceList.Entities.Add(dpInstance);
crmContext.Execute(UpdateRequest);
Basically, it looks like you have to re-set or re-attach the entities for CRM to pick it up, so this is a workaround for a bug in CRM

Sitefinity: Where can I find the master GUID for a content item?

I'm building a web service for a client that pulls data from the Sitefinity CMS. The problem is they want to pass in a Guid for the service and receive the info about this item. No problem except I only have been able to locate the "live" Guid for one Item (and that was by combing through the HTML in the back end).
I was going to look at the tables in SQL Server but I'm not sure which table to look at. The content items have several tables all related of course and there isn't any documentation on how to look at this. I can find plenty of documentation on querying the master Guid, but no place to find it.
Oh, and these are custom content types built by the Module Builder.
Any Help would be SOOOOO appreciated!
var master = DynamicModuleManager.GetManager().Lifecycle.GetMaster(<liveGuidHere>);
One of the biggest consumers of Sitefinity webservices is Sitefinity. The best place to start looking for that guid is to take a look at what web service calls are being made when you pull up your custom content item list in the backend. I used the chrome developer tools and check in the network tab.
One I found for a stores module made with module builder was something to the effect of http://www.testsite.com/Sitefinity/Services/DynamicModules/Data.svc/?managerType=Telerik.Sitefinity.DynamicModules.DynamicModuleManager&providerName=OpenAccessProvider&itemType=Telerik.Sitefinity.DynamicTypes.Model.Stores.Store&provider=OpenAccessProvider&sortExpression=LastModified%20DESC&skip=0&take=50
The json this returns is a list of all the masters with their ids (note in the list that the content items all have have a status of 0) http://www.sitefinity.com/documentation/documentationarticles/developers-guide/sitefinity-essentials/modules/content-lifecycle
When you go to Administration / Module Builder / Your Module, you will see a link to the API on the top right corner.
This link goes to a page full of API examples for your particular module which is kind of cool.
Basically you would have to find your item first using LINQ and the GetValue extension method.
Once you have the item you can get its ID or any other property.
using Telerik.Sitefinity.Utilities.TypeConverters;
using Telerik.Sitefinity.DynamicModules;
using Telerik.Sitefinity.Model;
....
var mgr = DynamicModuleManager.GetManager();
var countrymasters = from ctry in mgr.GetDataItems(TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.Destinations.Destination"))
where ctry.GetValue<string>("culture") == siteid &&
(ctry.Status == Telerik.Sitefinity.GenericContent.Model.ContentLifecycleStatus.Live && ctry.Visible == true)
select new
{
airport_cd = ctry.GetValue<string>("airport_cd"),
country_master_cd = ctry.GetValue<string>("country_master_cd")
};

Resources