How to update a related CRM entity reference with ODataLib? - c#

I'm using the ODataLib (http://odata.github.io) and the Client Generated Library to access Microsoft CRM's OData API (v9.0).
I'm trying to update the entity navigation property value (the GUID), but the update doesn't seem to do anything (no calls are made).
If I try to update the navigation property's value directly, I get an error saying that "CRM do not support direct update of Entity Reference properties, Use Navigation properties instead".
The entity is basically the middle entity in N:N relationship.
Basically what I'm doing in code is (semi pseudo-code):
Account a = _dao.GetAccount();
// This gets the dataservicecollection that tracks the changes
DataServiceCollection<MyRelationEntity> rel = _dao.GetMyRelationEntity();
rel.AccountId = a;
_dao.SaveChanges(SaveChangesOptions.PostOnlySetProperties);
Should I be using the AddLink, UpdateLink or something similar? They don't seem to do anything also.
I apologize if the terminology is not correct; I'm quite new to CRM.

I know nothing about ODataLib, but from a Dynamics 365 perspective the operation you're looking for is Associate.
Here's a pseudo-code example of a D365 Web API call to associate an opportunity to an account. Notice that the URI contains the accountId and the relationship name, while the body contains the opportunity's URI.
POST [Organization URI]/api/data/v9.0/accounts(00000000-0000-0000-0000-000000000002)/opportunity_customer_accounts/$ref HTTP/1.1
Content-Type: application/json
Accept: application/json
OData-MaxVersion: 4.0
OData-Version: 4.0
{
"#odata.id":"[Organization URI]/api/data/v9.0/opportunities(00000000-0000-0000-0000-000000000001)"
}
This article has more info.
And, when working with the D365 Web API, I find Jason Lattimer's RESTBuilder to be an indispensable tool.

Related

Trying to add a "SharepointDocumentLocation" to MS Dynamics365 via the Web API

I'm trying to create new Sharepoint document locations in my Dynamics365 (in the cloud) system, and I'm trying to link those to an existing Sharepoint Site (collection), as well as to a custom entity of my own.
I tried to do this:
POST /api/data/v9.2/sharepointdocumentlocations
Accept:application/json
Authorization: Bearer (valid JWT token)
Content-Type:application/json
OData-Version: 4.0
OData-MaxVersion: 4.0
{
"name": "WebDocuments",
"description": "Some useful description",
"sharepointdocumentlocation_parent_sharepointsite#odata.bind" : "sharepointsites(0f66e9e3-5dfc-ec11-82e5-0022489f9669)",
"relativeurl": "site",
"customEntity_SharePointDocumentLocations#odata.bind": "my_customentity(a654d179-ab61-ec11-8f8f-000d3a64d05c)"
}
but no matter what I try, I keep getting errors - mostly along the lines of:
An error occurred while validating input parameters: Microsoft.OData.ODataException: An undeclared property 'sharepointdocumentlocation_parent_sharepointsite' which only has property annotations in the payload but no property value was found in the payload. In OData, only declared navigation properties and declared named streams can be represented as properties without values.
I have been researching and found several blog posts offering help - unfortunately, none of that has helped me solve my issue.
I tried to use various field names:
sharepointdocumentlocation_parent_sharepointsite#odata.bind
ParentLocationOrSite
and quite a few more - yet without any success.
Any ideas? How can I create a new Sharepoint document location in Dynamics365, and set its ParentLocationOrSite and RegardingObjectId properties in the POST request?
The correct syntax for that field should be:
parentsiteorlocation_sharepointsite#odata.bind
as you have another lookup pointing to a custom entity, I suggest to use my tool Dataverse REST Builder to create the Web API requests.

How to find an entity name of a guid in Microsoft Dynamics 365 with c#?

I have a guid and I don't know that's for which entity.
How can I find owned entity name?
for example, there is a guid code: 7487cd8b-a0a2-eb11-b81e-005056a460ec. but what's the entity name?
If you don't want to write a GUID search yourself you could use XrmToolBox tool Universal Search. You enter a GUID and search across entities.
Install XrmToolBox
Install Universal Search tool inside XrmToolBox
Open Universal Search and enter your GUID in the Search Criteria box
Select one or more entities to search using the panel on left. Since you know your ID is in a regardingobjectid field, you may be able to limit your search to activity entities.
If this doesn't work you can search across all entities by using the Check All/None button, but be aware this is a long running and slow process
Universal Search XrmToolBox tool with annotations (low rep so can't post inline images)
How did you come across this Guid?
In Dynamics, you should never have a record Guid without its entity/logical name. They almost always come together - if you don't see it, it's probably not too far!
In C# Dynamics SDK, lookup fields should always be EntityReference objects.
(The trickiest) If you're using the Web API HTTP requests, make sure to include annotations in your request header:
Accept: application/json
Content-Type: application/json; charset=utf-8
OData-MaxVersion: 4.0
OData-Version: 4.0
Prefer: odata.include-annotations="*"
This will include additional fields:
_regardingobjectid_value#Microsoft.Dynamics.CRM.lookuplogicalname:"new_myentity",
_regardingobjectid_value:"ad2d51c9-1de2-449d-b80a-76232503aacf",
If using Xrm.WebAPI, the annotations should be included by default.
In model-driven form javascript, formContext.getAttribute("regardingobjectid").getValue() will give you an array of Xrm.LookupValue objects containing both.
entityType: "new_myentity", id: "{C8FE3743-2730-4625-85D5-325837015D8B}", name: "Name"}
In a related note, I think it's a good idea to never put guids in variables, parameters or otherwise -- they are rather meaningless on their own as you have found -- always carry around EntityReference / Xrm.LookupValue objects instead.
This maybe a rare and costly requirement :) You can use this code snippet for the exact purpose. Thanks to "Guid"o
// Retrieve Metadata for all entities
RetrieveAllEntitiesRequest allEntitiesRequest = new RetrieveAllEntitiesRequest();
allEntitiesRequest.EntityFilters = EntityFilters.Entity;
allEntitiesRequest.RetrieveAsIfPublished = true;
RetrieveAllEntitiesResponse allEntitiesResponse = (RetrieveAllEntitiesResponse)service.Execute(allEntitiesRequest);
// Record ID to check
Guid checkId = new Guid("541067bd-4db1-2208-e5b0-a601ead56d03");
string entityLogicalName = string.Empty;
foreach (EntityMetadata entityMetadata in allEntitiesResponse.EntityMetadata)
{
try
{
// Try to retrieve the record from the current entity
Entity test = service.Retrieve(entityMetadata.LogicalName, checkId, new ColumnSet(entityMetadata.PrimaryIdAttribute));
// if the previous instruction didn't throw an exception means that the record exists.
entityLogicalName = entityMetadata.LogicalName;
break;
}
catch { }
}
if (entityLogicalName != string.Empty)
{
string foundMessage = string.Format("Record with ID {0} belongs to entity {1}", checkId.ToString(), entityLogicalName);
Console.WriteLine(foundMessage);
}
else
{
string notFoundMessage = string.Format("Record with ID {0} not found in any entity", checkId.ToString());
Console.WriteLine(notFoundMessage);
}

MS Graph: How To Distinguish Teams-Enabled M365 Groups Using GraphClient?

The MS Graph rest API surfaces a resourceProvisioningOptions attribute to indicate whether a MS365 group is also a Team (see below). However, those values do not appear to be available in the GraphServiceClient.
I found this post, and used the sites endpoint to get the associated SharePoint URL for an M365 group. But some M365 groups have SharePoint sites and are not Teams.
The only other option I found was to use the teams endpoint and catch the exception when no team is found for the group ID. But then I still have to do the additional sites endpoint query to get the SharePoint URL.
Does anyone know of another/better way to distinguish between Team/non-Team M365 groups when using the GraphServiceClient?
I'd like to pile on to the helpful post by Baker_Kong.
This functionality is available in both the beta and v1.0 endpoints. It is not described in the v1.0 metadata (which we use to generate the model) and that is why you aren't seeing this in the object model. Until this is resolved, you could use the beta client or:
// Get only groups that have teams.
var groupsThatHaveTeams = await client.Groups.Request().Filter("resourceProvisioningOptions/Any(x:x eq 'Team')").GetAsync()
// When the metadata is fixed, each group will have a ResourceProvisioningOptions property that you can inspect for the 'Team' value.
// Until then, you'd need to look at the Group.AdditionalData dictionary for the resourceProvisioningOptions key and check if it has the 'Team' value.
var groupsThatMayHaveTeams = await client.Groups.Request().Select("id,resourceProvisioningOptions").GetAsync();
cross posted from https://github.com/microsoftgraph/msgraph-sdk-serviceissues/issues/44#issuecomment-752775347
#Tracy,
I have a test the SDK in a console app, I believe this property is under the group entity:
or you can add select option to omit the returned properties:
graphClient.Groups.Request().Select("id,resourceProvisioningOptions").GetAsync().Result;
BR

Can you get PR_TRANSPORT_MESSAGE_HEADERS 0x007D from the .Net Microsoft Graph API?

We are using alias email addresses to match incoming emails with a client. All the alias addresses are delivered into one Primary emailbox.
The alias address is not listed in the ToRecipients. If I open the email in OWA and look at the message details, I can see the alias in the To: property of the message header.
I tried using the graph.microsoft.com/beta endpoint to get the internetMessageHeader. It worked great(I was surprised.) Unfortunately, the To: property is missing from the response. (The From: property is missing too.)
The problem is the same as this question about using EWS. Exchange Web Services (EWS) API "To" header for alias
Is there an equivalent way to get the PR_TRANSPORT_MESSAGE_HEADERS 0x007D property using the Microsoft-Graph API .Net?
I tried:
var myvar = await graphClient.Users[inbox].Messages[message.Id].Request().Select("transportMessageHeaders").GetAsync();
But I got this error: Message: Could not find a property named 'transportMessageHeaders' on type 'Microsoft.OutlookServices.Message'.
Yes, you can access MAPI properties as extended properties in the Microsoft Graph API.
The URL construct you'd want would look something like:
GET /me/messages/{message-id}?$expand=singleValueExtendedProperties(
$filter=id eq 'String 0x007D')
Since you're using the .NET Graph library, you would modify your code above like so:
var myvar = await graphClient.Users["user"]
.Messages[message.Id]
.Request().Select("singleValueExtendedProperties")
.Expand("singleValueExtendedProperties($filter=id eq 'String 0x007D')").GetAsync();
string transportHeaders = myVar.SingleValueExtendedProperties.First().Value;

Get the data sent by MailChimp API V3 Webhook

I'm trying to write a webhook for Mailchimp events using API version three and I'm struggling a bit due to their lack of a library, documentation, and basic examples, but also my lack of experience.
I know we should secure the webhook by putting a secret in the URL, that's fine. By the way, MailChimp doesn't allow configuration of basic access authentication in their portal.
They say "While we do send HTTP POST for actual data, our webhook validator will only send HTTP GET requests. You'll need to allow both in order for your webhook to function properly." Ok, I guess I can use Request.HttpMethod to return a success status code if it's a GET and process some data if it's a POST.
Not sure how to pick stuff out of the request though, and ideally don't want to write heaps of classes and properties to cover all the event types, C# being statically typed, although I guess the dynamic keyword is also an option.
Do I need to deserialise JSON? I've only written one webhook before for another API with the help of a library, you could construct an API event using either a string, stream, or textreader, which came from the request. The library made everything very simple.
For reference, there's also this question which shows how to get some data using PHP: How to pass email address to webhook from mailchimp
The data that gets posted looks like this (supposedly, there doesn't seem to be any documentation for V3):
"type": "unsubscribe",
"fired_at": "2009-03-26 21:40:57",
"data[action]": "unsub",
"data[reason]": "manual",
"data[id]": "8a25ff1d98",
"data[list_id]": "a6b5da1054",
"data[email]": "api+unsub#mailchimp.com",
"data[email_type]": "html",
"data[merges][EMAIL]": "api+unsub#mailchimp.com",
"data[merges][FNAME]": "MailChimp",
"data[merges][LNAME]": "API",
"data[merges][INTERESTS]": "Group1,Group2",
"data[ip_opt]": "10.20.10.30",
"data[campaign_id]": "cb398d21d2",
"data[reason]": "hard"
I just basically need to get this data into variables so I can sync it with my database.
Here's my (skeleton) controller so far:
[Route("mailchimp/newsletter-webhook/super-secret-key-goes-here")]
public HttpStatusCodeResult ChargeBeeWebhook()
{
return new HttpStatusCodeResult(200);
}
Assuming you've already set up your MailChimp Webhooks as described here, you can get the posted data using Request.Form syntax. Using the example posted data from the question, here's how your controller code should look like:
[AllowAnonymous]
public void ChargeBeeWebhook()
{
// type variable will be "unsubscribe"
string type = Request.Form["type"];
// action variable will be "unsub"
string action = Request.Form["data[action]"];
// reason variable will be "manual"
string reason = Request.Form["data[reason]"];
// ...
// ...
// ... do the same with the rest of the posted variables
// ...
// sync the posted data above with your database
// ...
// ...
}

Categories

Resources