Näyte: Luo mukautettu laajennus Google Maps -ohjelmointirajapinnan käyttämiseen geospatiaalisena tietopalveluna
Voit käyttää valitsemasi tietopalvelun geospatiaalisia tietoja mukautetun laajennuksen avulla sen sijaan, ett käyttäisit oletusarvoista Bing Maps -ohjelmointirajapintaa Field Servicessä ja Project Servicessä.
Näyte on saatavana täällä: Mukautettu laajennus Google Maps -ohjelmointirajapinnan käyttämiseen geospatiaalisena tietopalveluna (Dynamics 365)
Edellytykset
Esimerkkiprojektin lataaminen ja siinä käytettävien NuGet-pakettien tallentaminen edellyttää Internet-yhteyttä.
Edellytykset
Dynamics 365 Field Service -ratkaisu on oltava asennettuna Dynamics 365 (online) -esiintymään. Lisätietoja: Field Servicen asennus ja määritykset
Anna oma Googlen ohjelmointirajapinnan avain näytteen GoogleDataContracts.cs-tiedostossa:
public const string GoogleApiKey = "<PROVIDE YOUR GOOGLE API KEY";
Esittelee
Tämä näyte näyttää, miten Universal Resource Schedulingin msdyn_GeocodeAddress- ja msdyn_RetrieveDistanceMatrix -toiminnoille luodaan mukautettu laajennus, käyttämään Google Maps -ohjelmointirajapintaa geospatiaalisia tietoja varten oletusarvoisen Bing Maps -ohjelmointirajapinnan sijaan.
Näytteen suorittaminen
Näyte luo laajennuksen kokoonpanotiedoston: CustomPlugin-FS-Geospatial.dll.
- Lataa tai kloonaa näytesäilö.
- Siirry sijaintiin, johon latasit tai kloonasit repo-tiedoston tietokoneeseen, siirry field-service/CustomPlugin-FS-Geospatial-kansioon ja avaa ratkaisu Visual Studiossa kaksoisnapsauttamalla CustomPlugin-FS-Geospatial.sln-tiedostoa.
- Valitse Visual Studiossa Muodosta>Muodosta ratkaisu. Ratkaisussa käytetyt NuGet-paketit latautuvat automaattisesti, jos Visual Studiossa on otettu käyttöön asetus, jolla NuGet-paketit palautetaan automaattisesti projektia muodostettaessa. Lisätietoja: Pakettien palauttamisen ottaminen käyttöön ja poistaminen käytöstä
Näytteen suorittamisen jälkeen
Kun näytteen suorittaminen (muodostaminen) on onnistunut, mukautetun laajennuksen kokoonpanotiedosto, CustomPlugin-FS-Geospatial.dll, on käytettävissä <Project>\bin\debug
-kansiossa. Rekisteröi mukautetun näytelaajennuksen kokoonpano Dynamics 365 (online) -esiintymässä, jotta laajennus voi käyttää Google Maps -ohjelmointirajapintaa oletusarvoisen Bing Maps -ohjelmointirajapinnan sijaan. Lisätietoja: Mukautetun laajennuksen rekisteröinti ja käyttöönottaminen.
msdyn_GeocodeAddress-toiminnon laajennuksen näytekoodi
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Text;
using Microsoft.Crm.Sdk.Samples.GoogleDataContracts;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
namespace Microsoft.Crm.Sdk.Samples
{
/// <summary>
/// msdyn_GeocodeAddress Plugin.
/// </summary>
public class msdyn_GeocodeAddress : IPlugin
{
const string PluginStatusCodeKey = "PluginStatus";
const string Address1Key = "Line1";
const string CityKey = "City";
const string StateKey = "StateOrProvince";
const string PostalCodeKey = "PostalCode";
const string CountryKey = "Country";
const string LatitudeKey = "Latitude";
const string LongitudeKey = "Longitude";
const string LcidKey = "Lcid";
public void Execute(IServiceProvider serviceProvider)
{
if (serviceProvider == null)
{
throw new InvalidPluginExecutionException("serviceProvider");
}
// Obtain the execution context service from the service provider.
IPluginExecutionContext PluginExecutionContext = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
// Obtain the organization factory service from the service provider.
IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
// Use the factory to generate the organization service.
IOrganizationService OrganizationService = factory.CreateOrganizationService(PluginExecutionContext.UserId);
// Obtain the tracing service from the service provider.
ITracingService TracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
ExecuteGeocodeAddress(PluginExecutionContext, OrganizationService, TracingService);
}
/// <summary>
/// Retrieve geocode address using Google Api
/// </summary>
/// <param name="pluginExecutionContext">Execution context</param>
/// <param name="organizationService">Organization service</param>
/// <param name="tracingService">Tracing service</param>
/// <param name="notificationService">Notification service</param>
public void ExecuteGeocodeAddress(IPluginExecutionContext pluginExecutionContext, IOrganizationService organizationService, ITracingService tracingService)
{
//Contains 5 fields (string) for individual parts of an address
ParameterCollection InputParameters = pluginExecutionContext.InputParameters;
// Contains 2 fields (double) for resultant geolocation
ParameterCollection OutputParameters = pluginExecutionContext.OutputParameters;
//Contains 1 field (int) for status of previous and this plugin
ParameterCollection SharedVariables = pluginExecutionContext.SharedVariables;
tracingService.Trace("ExecuteGeocodeAddress started. InputParameters = {0}, OutputParameters = {1}", InputParameters.Count().ToString(), OutputParameters.Count().ToString());
try
{
// If a plugin earlier in the pipeline has already geocoded successfully, quit
if ((double)OutputParameters[LatitudeKey] != 0d || (double)OutputParameters[LongitudeKey] != 0d) return;
// Get user Lcid if request did not include it
int Lcid = (int)InputParameters[LcidKey];
string _address = string.Empty;
if (Lcid == 0)
{
var userSettingsQuery = new QueryExpression("usersettings");
userSettingsQuery.ColumnSet.AddColumns("uilanguageid", "systemuserid");
userSettingsQuery.Criteria.AddCondition("systemuserid", ConditionOperator.Equal, pluginExecutionContext.InitiatingUserId);
var userSettings = organizationService.RetrieveMultiple(userSettingsQuery);
if (userSettings.Entities.Count > 0)
Lcid = (int)userSettings.Entities[0]["uilanguageid"];
}
// Arrange the address components in a single comma-separated string, according to LCID
_address = GisUtility.FormatInternationalAddress(Lcid,
(string)InputParameters[Address1Key],
(string)InputParameters[PostalCodeKey],
(string)InputParameters[CityKey],
(string)InputParameters[StateKey],
(string)InputParameters[CountryKey]);
// Make Geocoding call to Google API
WebClient client = new WebClient();
var url = $"https://{GoogleConstants.GoogleApiServer}{GoogleConstants.GoogleGeocodePath}/json?address={_address}&key={GoogleConstants.GoogleApiKey}";
tracingService.Trace($"Calling {url}\n");
string response = client.DownloadString(url); // Post ...
tracingService.Trace("Parsing response ...\n");
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(GeocodeResponse)); // Deserialize response json
object objResponse = jsonSerializer.ReadObject(new MemoryStream(Encoding.UTF8.GetBytes(response))); // Get response as an object
GeocodeResponse geocodeResponse = objResponse as GeocodeResponse; // Unbox into our data contracted class for response
tracingService.Trace("Response Status = " + geocodeResponse.Status + "\n");
if (geocodeResponse.Status != "OK")
throw new ApplicationException($"Server {GoogleConstants.GoogleApiServer} application error (Status {geocodeResponse.Status}).");
tracingService.Trace("Checking geocodeResponse.Result...\n");
if (geocodeResponse.Results != null)
{
if (geocodeResponse.Results.Count() == 1)
{
tracingService.Trace("Checking geocodeResponse.Result.Geometry.Location...\n");
if (geocodeResponse.Results.First()?.Geometry?.Location != null)
{
tracingService.Trace("Setting Latitude, Longitude in OutputParameters...\n");
// update output parameters
OutputParameters[LatitudeKey] = geocodeResponse.Results.First().Geometry.Location.Lat;
OutputParameters[LongitudeKey] = geocodeResponse.Results.First().Geometry.Location.Lng;
}
else throw new ApplicationException($"Server {GoogleConstants.GoogleApiServer} application error (missing Results[0].Geometry.Location)");
}
else throw new ApplicationException($"Server {GoogleConstants.GoogleApiServer} application error (more than 1 result returned)");
}
else throw new ApplicationException($"Server {GoogleConstants.GoogleApiServer} application error (missing Results)");
}
catch (Exception ex)
{
// Signal to subsequent plugins in this message pipeline that geocoding failed here.
OutputParameters[LatitudeKey] = 0d;
OutputParameters[LongitudeKey] = 0d;
//TODO: You may need to decide which caught exceptions will rethrow and which ones will simply signal geocoding did not complete.
throw new InvalidPluginExecutionException(string.Format("Geocoding failed at {0} with exception -- {1}: {2}"
, GoogleConstants.GoogleApiServer, ex.GetType().ToString(), ex.Message), ex);
}
}
}
}
msdyn_RetrieveDistanceMatrix-toiminnon laajennuksen näytekoodi
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Text;
using Microsoft.Crm.Sdk.Samples.GoogleDataContracts;
using Microsoft.Xrm.Sdk;
using static Microsoft.Crm.Sdk.Samples.GoogleDataContracts.DistanceMatrixResponse.CResult.CElement;
namespace Microsoft.Crm.Sdk.Samples
{
/// <summary>
/// msdyn_RetrieveDistanceMatrix Plugin.
/// </summary>
public class msdyn_RetrieveDistance : IPlugin
{
const string PluginStatusCodeKey = "PluginStatus";
const string SourcesKey = "Sources";
const string TargetsKey = "Targets";
const string MatrixKey = "Result";
/// <summary>
/// Initializes a new instance of the msdyn_RetrieveDistance class
/// </summary>
/// <param name="unsecure"></param>
/// <param name="secure"></param>
public msdyn_RetrieveDistance(string unsecure, string secure)
{
// TODO: Implement your custom configuration handling.
}
/// <summary>
/// Execute the plugin
/// </summary>
/// <param name="serviceProvider"></param>
public void Execute(IServiceProvider serviceProvider)
{
if (serviceProvider == null)
{
throw new InvalidPluginExecutionException("serviceProvider");
}
// Obtain the execution context service from the service provider.
IPluginExecutionContext PluginExecutionContext = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
// Obtain the organization factory service from the service provider.
IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
// Use the factory to generate the organization service.
IOrganizationService OrganizationService = factory.CreateOrganizationService(PluginExecutionContext.UserId);
// Obtain the tracing service from the service provider.
ITracingService TracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
ExecuteDistanceMatrix(PluginExecutionContext, OrganizationService, TracingService);
}
public void ExecuteDistanceMatrix(IPluginExecutionContext pluginExecutionContext, IOrganizationService organizationService, ITracingService tracingService)
{
//Contains 2 fields (EntityCollection) for sources and targets
ParameterCollection InputParameters = pluginExecutionContext.InputParameters;
// Contains 1 field (EntityCollection) for results
ParameterCollection OutputParameters = pluginExecutionContext.OutputParameters;
//Contains 1 field (int) for status of previous and this plugin
ParameterCollection SharedVariables = pluginExecutionContext.SharedVariables;
tracingService.Trace("ExecuteDistanceMatrix started. InputParameters = {0},OutputParameters = {1}", InputParameters.Count().ToString(), OutputParameters.Count().ToString());
try
{
// If a plugin earlier in the pipeline has already retrieved a distance matrix successfully, quit
if (OutputParameters[MatrixKey] != null)
if (((EntityCollection)OutputParameters[MatrixKey]).Entities != null)
if (((EntityCollection)OutputParameters[MatrixKey]).Entities.Count > 0) return;
// Make Distance Matrix call to Google API
WebClient client = new WebClient();
var url = String.Format($"https://{GoogleConstants.GoogleApiServer}{GoogleConstants.GoogleDistanceMatrixPath}/json"
+ "?units=imperial"
+ $"&origins={string.Join("|", ((EntityCollection)InputParameters[SourcesKey]).Entities.Select(e => e.GetAttributeValue<double?>("latitude") + "," + e.GetAttributeValue<double?>("longitude")))}"
+ $"&destinations={string.Join("|", ((EntityCollection)InputParameters[TargetsKey]).Entities.Select(e => e.GetAttributeValue<double?>("latitude") + "," + e.GetAttributeValue<double?>("longitude")))}"
+ $"&key={GoogleConstants.GoogleApiKey}");
tracingService.Trace($"Calling {url}\n");
string response = client.DownloadString(url); // Post ...
tracingService.Trace("Parsing response ...\n");
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(DistanceMatrixResponse)); // Deserialize response json
object objResponse = jsonSerializer.ReadObject(new MemoryStream(Encoding.UTF8.GetBytes(response))); // Get response as an object
DistanceMatrixResponse distancematrixResponse = objResponse as DistanceMatrixResponse; // Unbox as our data contracted class for response
tracingService.Trace("Response Status = " + distancematrixResponse.Status + "\n");
if (distancematrixResponse.Status != "OK")
throw new ApplicationException($"Server {GoogleConstants.GoogleApiServer} application error (Status={distancematrixResponse.Status}). {distancematrixResponse.ErrorMessage}");
tracingService.Trace("Checking distancematrixResponse.Results...\n");
if (distancematrixResponse.Rows != null)
{
tracingService.Trace("Parsing distancematrixResponse.Results.Elements...\n");
// build and update output parameter
var result = new EntityCollection();
result.Entities.AddRange(distancematrixResponse.Rows.Select(r => ToEntity(r.Columns.Select(c => ToEntity(c.Status, c.Duration, c.Distance)).ToArray())));
OutputParameters[MatrixKey] = result;
}
else throw new ApplicationException($"Server {GoogleConstants.GoogleApiServer} application error (missing Rows)");
}
catch (Exception ex)
{
// Signal to subsequent plugins in this message pipeline that retrieval of distance matrix failed here.
OutputParameters[MatrixKey] = null;
//TODO: You may need to decide which caught exceptions will rethrow and which ones will simply signal geocoding did not complete.
throw new InvalidPluginExecutionException(string.Format("Geocoding failed at {0} with exception -- {1}: {2}"
, GoogleConstants.GoogleApiServer, ex.GetType().ToString(), ex.Message), ex);
}
// For debugging purposes, throw an exception to see the details of the parameters
CreateExceptionWithDetails("Debugging...", InputParameters, OutputParameters, SharedVariables);
}
private Entity ToEntity(string status, CProperty duration, CProperty meters)
{
var e = new Entity("organization");
e["status"] = status;
if (status.ToUpper() == "OK")
{
e["miles"] = meters.Value * 0.000621371d; // Convert to miles
e["duration"] = duration.Value;
}
else
{ // either NOT_FOUND or ZERO_RESULTS
e["miles"] = 0d;
e["duration"] = 0d;
}
return e;
}
private Entity ToEntity(params Entity[] entities)
{
var c = new EntityCollection();
c.Entities.AddRange(entities);
var e = new Entity("organization");
e[MatrixKey] = c;
return e;
}
private void CreateExceptionWithDetails(string message, ParameterCollection inputs, ParameterCollection outputs, ParameterCollection shareds)
{
StringBuilder sb = new StringBuilder(message + "\n");
sb.AppendLine("InputParameters -- ");
foreach (var item in inputs)
{
sb.AppendLine("\t" + item.Key + " : '" + item.Value + "' ");
if (((EntityCollection)item.Value).Entities != null)
((EntityCollection)item.Value).Entities.ToList().ForEach(e => sb.AppendLine("\t\t" + e.GetAttributeValue<double>("latitude").ToString() + "," + e.GetAttributeValue<double>("longitude").ToString()));
}
if (outputs != null)
{
sb.AppendLine("OutputParameters -- ");
foreach (var item in outputs)
{
sb.AppendLine("\t" + item.Key + " : '" + item.Value + "' ");
if (item.Value != null)
if (((EntityCollection)item.Value).Entities != null)
((EntityCollection)item.Value).Entities.ToList().ForEach(r => {
sb.AppendLine("\t\t" + r.GetAttributeValue<EntityCollection>(MatrixKey).ToString());
if (r.GetAttributeValue<EntityCollection>(MatrixKey).Entities != null)
r.GetAttributeValue<EntityCollection>(MatrixKey).Entities.ToList().ForEach(e => sb.AppendLine("\t\t" + e.GetAttributeValue<double>("distance").ToString() + "," + e.GetAttributeValue<double>("duration").ToString()));
});
}
}
sb.AppendLine("SharedVariables -- ");
foreach (var item in shareds) sb.AppendLine("\t" + item.Key + " : '" + item.Value + "' ");
throw new InvalidPluginExecutionException(sb.ToString());
}
}
}
Tietosuojatietoja koskeva vastuuvapausilmoitus
Näytekoodia voi käyttää yhteydenpitoon kolmansien osapuolien kanssa, joiden tietosuoja- ja tietoturvakäytännöt voivat poiketa Microsoft Dynamics 365:tä vastaavista käytännöistä. JOS LÄHETÄT TIETOJA KOLMANNEN OSAPUOLEN PALVELUIHIN, NÄITÄ TIETOJA HALLINNOIDAAN NÄIDEN PALVELUJEN TIETOSUOJAKÄYTÄNTÖJEN MUKAISESTI. Microsoft Dynamics 365:n ulkopuolelle jaetut tiedot eivät kuulu Microsoft Dynamics 365:n sopimusten tai Microsoft Dynamics 365:n luottamuskeskuksen piiriin. Suosittelemme näiden muiden tietosuojatietojen lukemista.
Katso myös
Mukautetun laajennuksen luominen käyttämään ensisijaista geospatiaalista tietopalvelua