Connect to CRM and get data - c#

I'm writing an ASP.NET app that should connect to dynamics CRM and fetch the contacts info. In the view it should return a list of the contacts with their info. There is some data in the testing CRM I'm provided with but I can't get the entities (e.g. table name and its columns), so I can't create my models in VS, since I don't know what props to put there.
Is there a way to get the entites with the code, or how can this problem be solved?
I've tried early-bound generator, and it won't work. I tried with ADO.NET entity data model but doesn't work either.

In order to actually work with the CRM entities you're going to need to configure your project to work with an Organization Service (Might need a different tutorial depending on CRM version) that connects to your CRM instance.
Afterwards, like David Yenglin was explaining, you should be able to use methods like IOrganizationService.Retrieve(String, Guid, ColumnSet) which allows you to retrieve a record or IOrganizationService.RetrieveMultiple(QueryBase) which allows you to retrieve multiple records. In your case, I think you just need the Retrieve method.
Another way you can access the data is through the CRM Web API (which I have less experience with) which would allow you to query the data from CRM.
There's a ton of great examples of different scenarios over at Microsoft's github repo, PowerApps-Samples. I'd recommend checking it out later when you get your project configured.

This article will give you step by step way to connect to CRM.
Once you get sucessfully connected to CRM, try below code which will give you complete information about all the contact Records.
try
{
ClientCredentials clientCredentials = new ClientCredentials();
clientCredentials.UserName.UserName = "<ProvideUserName>#<ProvideYourOrgName>.onmicrosoft.com";
clientCredentials.UserName.Password = "<ProvideYourPassword>";
// For Dynamics 365 Customer Engagement V9.X, set Security Protocol as TLS12
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
// Get the URL from CRM, Navigate to Settings -> Customizations -> Developer Resources
// Copy and Paste Organization Service Endpoint Address URL
organizationService = (IOrganizationService)new OrganizationServiceProxy(new Uri("https://<ProvideYourOrgName>.api.<CRMRegion>.dynamics.com/XRMServices/2011/Organization.svc"),
null, clientCredentials, null);
if (organizationService != null)
{
Guid userid = ((WhoAmIResponse)organizationService.Execute(new WhoAmIRequest())).UserId;
if (userid != Guid.Empty)
{
Console.WriteLine("Connection Established Successfully...");
// your logic here.
queryExpressionTest(organizationService);
}
}
else
{
Console.WriteLine("Failed to Established Connection!!!");
}
}
catch (Exception ex)
{
Console.WriteLine("Exception caught - " + ex.Message);
}
Console.ReadKey();
}
private static void queryExpressionTest(IOrganizationService organizationService)
{
QueryExpression qe = new QueryExpression();
qe.EntityName = "contact";
qe.ColumnSet= new ColumnSet(true); // this will give you all the columns of contact record
//qe.ColumnSet= new ColumnSet("name", "accountnumber"); you could also restrict which particualr attributes you wish to retrieve from contact record.
EntityCollection coll = organizationService.RetrieveMultiple(qe);
foreach (Entity cont in coll.Entities)
{
Console.WriteLine("Name of contact: " + cont.GetAttributeValue<string>("fullname"));
Console.WriteLine("Email of contact " + cont.GetAttributeValue<string>("email"));
/**
Now you can add all the attributes you wish to show
*/
}
}

Related

CRM Plugin: how to update associated contacts' email address when you update account email address [duplicate]

I'm using Microsoft Dynamics CRM 2016 on-premise version.
I have an "account" entity which has associated "contacts". When the current "account" entity's "address" is updated, I want all the associated contacts addresses to be updated with that address.
I want to do this in a plug-in which runs on the "account" entity when you update the address. When you do so, ALL the associated contacts have their address updated to that address.
I've done a bit of searching for this, but there's nothing out there which shows the ADDRESS getting updated. The examples out there typically show, for example, a phone number being updated. What makes the address more complicated is that the addresses are stored in an address entity, so I think I have to get an addressId primary key of some kind and put that in each of the associated contacts address FK field. I've no idea how and can't find any similar examples.
Does anyone have a snippet of code that will go into the plugin?
[NB I'm planning on putting it in the public void Execute(IServiceProvider serviceProvider) method in the plugin code.]
You do not need the id of a contact's address when updating it. Two types of addresses are already incorporated in the contact entity. They are typically used for postal and visit addresses. The address field names are prefixed with address1_ and address2_. So, just set these fields like this on the contact entity:
contact["address1_line1"] = "John Doe";
A plugin could look like this:
public class AccountPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
var orgService = factory.CreateOrganizationService(null);
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
Entity account = context.PostEntityImages.First().Value;
var query = new QueryExpression("contact");
query.Criteria.AddCondition("accountid", ConditionOperator.Equal, context.PrimaryEntityId);
var result = orgService.RetrieveMultiple(query);
foreach (Entity contact in result.Entities)
{
contact["address1_line1"] = account.GetAttributeValue<string>("address1_line2");
orgService.Update(contact);
}
}
}
Register it on the post update message on entity account and add a post entity image to the step.
Thanks to Henk Van Boeijen's excellent answer above, I post the following code (which is Henk's code posted into my plug-in).
Here is the code for a plugin which will update all of the addresses for all of the contacts connected to an organisation, when you update the organization's address.
Note that in this example, the Organization entity has been called Account.
This is to help anyone in future who needs to accomplish this.
public class UpdateContactAddresses : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// Create a tracing instance to log progress of this plugin.
ITracingService tracing = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
try
{
// Obtain the execution context from the service provider.
IPluginExecutionContext pluginExecutionContext = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
// Obtain the organization service reference.
IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = factory.CreateOrganizationService(null);
if (pluginExecutionContext.InputParameters.Contains("Target") && pluginExecutionContext.InputParameters["Target"] is Entity)
{
// Obtain the target entity from the input parameters.
Entity account = (pluginExecutionContext.InputParameters["Target"] as Entity);
// Verify that the target entity represents an account. If not, this plug-in was not registered correctly.
if (account.LogicalName != "account")
{
tracing.Trace("This entity is not an Account entity. It is likely that this plug-in was not registered correctly (was an incorrect \"Primary Entity\" selected? It should be an Account entity).");
return;
}
var query = new QueryExpression("contact");
query.Criteria.AddCondition("accountid", ConditionOperator.Equal, pluginExecutionContext.PrimaryEntityId);
var result = service.RetrieveMultiple(query);
tracing.Trace("The QueryExpression found " + result.TotalRecordCount.ToString() + " associated contacts.");
foreach (Entity contact in result.Entities)
{
tracing.Trace("Updating contact " + contact.ToString() + " address...");
contact["address1_line1"] = account.GetAttributeValue<string>("address1_line1");
contact["address1_line2"] = account.GetAttributeValue<string>("address1_line2");
contact["address1_line3"] = account.GetAttributeValue<string>("address1_line3");
contact["address1_city"] = account.GetAttributeValue<string>("address1_city");
contact["address1_county"] = account.GetAttributeValue<string>("address1_county");
contact["address1_postalcode"] = account.GetAttributeValue<string>("address1_postalcode");
contact["address1_country"] = account.GetAttributeValue<string>("address1_country");
service.Update(contact);
tracing.Trace("Contact " + contact.ToString() + " address updated.");
}
}
tracing.Trace("Completed execution of plugin " + this.GetType().Name + ".");
}
catch (FaultException<OrganizationServiceFault> ex)
{
throw new InvalidPluginExecutionException("An error occurred in plugin " + this.GetType().Name + ".", ex);
}
catch (Exception ex)
{
tracing.Trace("An error occurred executing plugin " + this.GetType().Name + ".");
tracing.Trace("\t\tError: " + ex.Message);
throw ex;
}
}
}

Microsoft Graph API - multi page user query with $expand failing after updating to latest version

For a while now I had an application built that was syncing employee information between a HR system we use and our AAD to keep titles etc in check
At some point my patch operations to update users started to silently fail (this is another issue..) - but that lead me down the rabbit hole of updating my nuget packages and after that point I am now unable to use my same query to pull all of our users from AAD. I need manager information so I use $expand and I also need to filter so as far as I am aware that means I need to use $count
As it stands because I use $count (there is no #odata.nextlink) when I try to pull the second page of data, the API now tells me $count is not a supported query option (or something along those lines). I've tried removing the query option and other stabs in the dark to no avail.
I've scoured around and cannot find a fix / workaround
Any help would be greatly appreciated - in the end I just need the below info so I can create custom objects I use to compare across systems.
GraphServiceClient graphServiceClient = AzureAuth.SetupGraphClient();
//graphServiceClient.BaseUrl = "https://graph.microsoft.com/beta";
List<Option> queryOptions = new List<Option>();
QueryOption count = new QueryOption("$count", "true");
QueryOption manager = new QueryOption("$expand", "manager($levels=max;$select=id,displayName)");
queryOptions.Add(count);
queryOptions.Add(manager);
var users = await graphServiceClient.Users.Request(queryOptions).Header("ConsistencyLevel", "eventual").Select(x => new
{
x.Id,
x.Department,
x.Mail,
x.EmployeeId,
x.GivenName,
x.Surname,
x.Manager,
x.BusinessPhones,
x.JobTitle,
x.AccountEnabled,
x.DisplayName,
x.City,
x.State
}).Filter($"endsWith(mail, '#sightsciences.com')").GetAsync();
//Return first paginated result set
userHolder.AddRange(users.CurrentPage);
//If more than a single page of data is returned, continue pulling until no further pages are present
try
{
while (users.NextPageRequest != null)
{
users = await users.NextPageRequest.GetAsync();
userHolder.AddRange(users.CurrentPage);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}

Query a list of Project numbers from a table of Projects via CRM Custom Plugin

I have a table of project entities in CRM, each project entity has a text field called "project number". I want to query out a list of all the project numbers available in the table.
All of the sources that I have looked at, mention that I need to use a ServiceContext or XrmServiceContext()but it seems that those are generated using the CrmSvcUtil tool. The tutorial I used for this portion is found here.
From my past experience with CRM Plugin development, I have found that I am not allowed to do any local tasks within the plugin execution, therefore using the CrmSvcUtil tool conflicts with this.
Am I approaching this situation all wrong? I have access to OrganizationServiceContext but I am not sure if this will give me access to query my project entities.
EDIT:
My references listed below but LocalPluginContext cannot be found. Quick google search suggested I just add the items from the sdk but I have added everything.
There are 2 Ways you could achieve This.
1. Console Applicaiton where you do not need context rather you sign in and then get IOrganizationService
static void Main(string[] args)
{
IOrganizationService organizationService = null;
try
{
ClientCredentials clientCredentials = new ClientCredentials();
clientCredentials.UserName.UserName = "AdminCRM#dabc.onmicrosoft.com";
clientCredentials.UserName.Password = "pwd";
//For Dynamics 365 Customer Engagement V9.X, set Security Protocol as TLS12
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//Get the URL from CRM, Navigate to Settings -> Customizations -> Developer Resources
//Copy and Paste Organization Service Endpoint Address URL
organizationService = (IOrganizationService)new OrganizationServiceProxy(new Uri("https:/[OrgUrl]/XRMServices/2011/Organization.svc"),
null, clientCredentials, null);
if (organizationService != null)
{
Guid userid = ((WhoAmIResponse)organizationService.Execute(new WhoAmIRequest())).UserId;
if (userid != Guid.Empty)
{
Console.WriteLine("Connection Established Successfully...");
FetchXmlTestQuery(organizationService);
queryExpressionTest(organizationService);
}
}
else
{
Console.WriteLine("Failed to Established Connection!!!");
}
}
catch (Exception ex)
{
Console.WriteLine("Exception caught - " + ex.Message);
}
Console.ReadKey();
}
private static void queryExpressionTest(IOrganizationService organizationService)
{
QueryExpression qe = new QueryExpression();
qe.EntityName = "account";
qe.ColumnSet= new ColumnSet("name", "accountnumber");
EntityCollection coll = organizationService.RetrieveMultiple(qe);
foreach (Entity acunt in coll.Entities)
{
Console.WriteLine("Name of Account: " + acunt.GetAttributeValue<string>("name"));
Console.WriteLine("Number of Account: " + acunt.GetAttributeValue<string>("accountnumber"));
}
}
private static void FetchXmlTestQuery(IOrganizationService CrmConn)
{
// Retrieve all accounts owned by the user with read access rights to the accounts and
// where the last name of the user is not Cannon.
string fetch = #"
<fetch>
<entity name='account' >
<attribute name='name' />
<attribute name='accountnumber' />
<link-entity name='contact' from='parentcustomerid' to='accountid' link-type='inner' alias='Contact' >
<attribute name='fullname' alias = 'Contact.Fullname' />
</link-entity>
</entity>
</fetch> ";
EntityCollection Coll = CrmConn.RetrieveMultiple(new FetchExpression(fetch));
foreach (Entity acunt in Coll.Entities)
{
Console.WriteLine("Name of Account: " + acunt.GetAttributeValue<string>("name"));
Console.WriteLine("Name of Contact: " + acunt.GetAttributeValue<AliasedValue>("Contact.Fullname").Value);
Console.WriteLine("Number of Account: " + acunt.GetAttributeValue<string>("accountnumber"));
}
}
Now you could also use Plugin Context
protected override void ExecuteCrmPlugin(LocalPluginContext localContext)
{
if (localContext == null)
{
throw new ArgumentNullException("localContext");
}
// TODO: Implement your custom plug-in business logic.
IPluginExecutionContext context = localContext.PluginExecutionContext;
ITracingService tracingService = localContext.TracingService;
IOrganizationService orgService = localContext.OrganizationService;
FetchXmlTestQuery(orgService);
queryExpressionTest(orgService);
}
private void FetchXmlTestQuery(IOrganizationService orgService)
{
// Retrieve all accounts owned by the user with read access rights to the accounts and
// where the last name of the user is not Cannon.
string fetch = #"
<fetch>
<entity name='account' >
<attribute name='name' />
<attribute name='accountnumber' />
<link-entity name='contact' from='parentcustomerid' to='accountid' link-type='inner' alias='Contact' >
<attribute name='fullname' alias = 'Contact.Fullname' />
</link-entity>
</entity>
</fetch> ";
EntityCollection Coll = orgService.RetrieveMultiple(new FetchExpression(fetch));
foreach (Entity acunt in Coll.Entities)
{
string accountname= acunt.GetAttributeValue<string>("name");
string accountnr= acunt.GetAttributeValue<string>("accountnumber");
}
}
private static void queryExpressionTest(IOrganizationService organizationService)
{
QueryExpression qe = new QueryExpression();
qe.EntityName = "account";
qe.ColumnSet = new ColumnSet("name", "accountnumber");
EntityCollection coll = organizationService.RetrieveMultiple(qe);
foreach (Entity acunt in coll.Entities)
{
string accountname = acunt.GetAttributeValue<string>("name");
string accountnr = acunt.GetAttributeValue<string>("accountnumber");
}
}
Inside the plugin you will get the whole execution context of the pipeline & Organization Service access to extend the business functionality in the same pipeline.
These below code snippets are the boiler plate code will give you the various necessary parts like tracing service for logging, context to get target entity, images, etc and IOrganizationServiceto make service calls like Update, Retrieve, etc achieve the platform extension.
As you know there will be a single public class in Plugin & a single public method Execute(IServiceProvider serviceProvider) and we will get everything using this single parameter serviceProvider
// Obtain the tracing service
ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
// Obtain the execution context from the service provider.
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
// Obtain the organization service reference which you will need for
// web service calls.
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
When you want to query for other project numbers from the database, use the service.RetrieveMultiple method to query. You can pass the fetchxml query or use queryexpression to do it.
You can find lot of examples online. Starter example.
Here is what I would recommend.
Install XrmToolBox (make sure you unblock the zip file before unzipping it)
Install the Early Bound Generator and the Visual Studio Solution Accelerator from the XrmToolBox plugin store within the tool.
Run the Visual Studio Solution Accelerator either adding the core projects to your existing solution, or using it to create a new solution. I'd suggest adding the example plugins so you can see how to create a plugin. Also suggest using EarlyBinding.
Run the EarlyBoundGenerator, pasting in the path that was copied to your clipboard from the Visual Studio Solution Accelerator. Add whatever custom entities you'll need for your plugin. Create All.
Create your plugin in the plugin project.

Updating MetaData on Connected account fails

I am using stripe connect(destination payment) with the help of stripe.net library from Jaymedavis.
The problem that I am facing is that I am not able to retrieve the destination payment ID to update the metadata in the connected account. The following line returns a null preventing me from updating meta data on the connected account. But the strange thing is that when I log in to the dashboard the destination payment ID exists. I am not sure why I am not able to retreive it in code.
Is the charge creation asynchronous?. I am not sure. Stripe's connect documentation does not help either. The following line returns a null. My code is down below. Seeking help.
String deschargeID = result.Transfer.DestinationPayment;
Here is the code that I am using
var service = new StripeChargeService(ZambreroSecretKey);
var result = (Stripe.StripeCharge) null;
try {
result = service.Create(newCharge);
if (result.Paid) {
//get the chargeID on the newgen account and update the metadata.
//Returns null even though it exists in the dashboard
String deschargeID = result.Transfer.DestinationPayment;
var chargeService = new StripeChargeService(newgenSecretKey);
StripeCharge charge = chargeService.Get(deschargeID);
charge.Metadata = myDict;
Response.Redirect("PgeCustSuccess.aspx?OrderID=" + OrderID);
}
} catch (StripeException stripeException) {
Debug.WriteLine(stripeException.Message);
stripe.Text = stripeException.Message;
}
The charge object's transfer attribute is not expanded by default, meaning it's just a string with the ID of the transfer object ("tr_..."), not a full transfer object.
According to Stripe.net's documentation, you can expand the transfer attribute by adding this line:
service.ExpandTransfer = True
before sending the charge creation request.

Changing FullName programmatically in CRM Online (2011)

I am attempting to change the "FullName" field of existing CRM system users in our Dynamics CRM 2011 Online account. I have already made the change in settings to update all future users to the format "Last, First" ... so this is for changing the existing users.
I read the best way is to do this programmatically using the CRM SDK. When I perform the actual Update command, I receive an unspecified error from the SDK: Additional information: The property IsLicensed cannot be modified.
Although I'm querying all columns for entity object SystemUsers, I'm only changing the FullName field. Has anyone else had experience with this? My code is below, I'm running this as a console app to step through each SystemUser.
static void Main(string[] args)
{
string connStr = ConfigurationManager.ConnectionStrings["CRMOnline"].ToString();
CrmConnection conn = CrmConnection.Parse(connStr);
conn.DeviceCredentials = DeviceIdManager.LoadOrRegisterDevice();
using (OrganizationService svc = new OrganizationService(conn))
{
QueryExpression qry = new QueryExpression();
qry.ColumnSet = new ColumnSet(true); // get all columns
qry.EntityName = CRMO.SystemUser.EntityLogicalName; // get entity object SystemUser
qry.Criteria.AddCondition(new ConditionExpression("calendarid", ConditionOperator.NotNull)); // but non-builtin users
EntityCollection col = svc.RetrieveMultiple(qry); // executes query
foreach (Entity ent in col.Entities)
{
Console.WriteLine();
Console.WriteLine("Current Fullname: " + ent.Attributes["fullname"].ToString());
Console.Write("Change? y/N: ");
string ans = Console.ReadLine();
if (ans.ToLower() == "y")
{
Console.Write("New Name: ");
string newname = Console.ReadLine();
if (newname != "")
{
ent.Attributes["fullname"] = newname;
svc.Update(ent); // fails here with SDK error: "Additional information: The property IsLicensed cannot be modified."
}
}
}
Console.WriteLine();
Console.WriteLine("--- Done ---");
Console.ReadLine();
}
}
Rule 28 of the Crm SDK, don't ever perform updates by performing a select, which returns back more fields than what you are planning to update. Any fields in the attribute collection of the Entity will be updated even if they haven't changed. Instead, instantiate a new entity locally, set the id and whatever attributes you want to update and update it.
On a side note, you can't update the full name of a System User. You have to update the individual pieces. So your code should really look like this:
static void Main(string[] args)
{
string connStr = ConfigurationManager.ConnectionStrings["CRMOnline"];
CrmConnection conn = CrmConnection.Parse(connStr);
conn.DeviceCredentials = DeviceIdManager.LoadOrRegisterDevice();
using (OrganizationService svc = new OrganizationService(conn))
{
QueryExpression qry = new QueryExpression();
qry.ColumnSet = new ColumnSet("firstname", "lastname", "fullname"); // get only what is needed for performance reasons
qry.EntityName = CRMO.SystemUser.EntityLogicalName; // get entity object SystemUser
qry.Criteria.AddCondition(new ConditionExpression("calendarid", ConditionOperator.NotNull)); // but non-builtin users
EntityCollection col = svc.RetrieveMultiple(qry); // executes query
foreach (Entity ent in col.Entities)
{
Console.WriteLine();
Console.WriteLine("Current Fullname: " + ent["fullname"].ToString());
Console.Write("Update? Y/N: ");
string ans = Console.ReadLine();
if (ans.ToLower() == "y")
{
// Create a new entity, setting the id and whatever attributes that need to be updated
var updateEntity = new Entity { Id = ent.Id };
updateEntity["firstname"] = ent["firstname"];
updateEntity["lastname"] = ent["lastname"];
svc.Update(updateEntity);
}
}
Console.WriteLine();
Console.WriteLine("--- Done ---");
Console.ReadLine();
}
}
Notes:
Only retrieve the columns you actually need
Create an update entity that only contains the fields you want to update
Remember that FullName is readonly
This may also be helpful
This is so others reading this can use this solution to change the FullName in CRM Online.
So in my case, where I needed to change the FullName of existing CRM users from "First Last" to "Last, First", I was able to perform regular Office 365 admin functions to complete this.
First, I changed the format in CRM Settings > System Settings to "Last Name, First Name".
Then, for each user I needed to have changed, I used the Office 365 Admin Center and edited their licenses. Un-assign the CRM license from the user and click SAVE. Wait about a minute or two for the changes to take affect. Next, go back into that same user management and re-assign the CRM license to the user, click SAVE. Wait a few minutes and you will see the FullName in CRM should be in the correct format.

Categories