Κοινή χρήση μέσω


Δείγμα: Δημιουργήστε προσαρμοσμένη προσθήκη για να χρησιμοποιήσετε API του Google Maps ως πάροχο γεωχωρικών δεδομένων

Μπορείτε να χρησιμοποιήσετε μια προσαρμοσμένη προσθήκης για χρήση γεω-χωρικών δεδομένων από μια υπηρεσία παροχής δεδομένων της επιλογής σας, αντί να χρησιμοποιήσετε το προεπιλεγμένο API Χαρτών Bing στα Field Service και Project Service.

Το δείγμα είναι διαθέσιμο εδώ: Προσαρμοσμένη προσθήκης για να χρησιμοποιήσετε το API του Google Maps ως υπηρεσία παροχής γεω-χωρικών δεδομένων (Dynamics 365)

Προαπαιτούμενα στοιχεία

Απαιτείται σύνδεση στο Internet για να κάνετε λήψη του δείγματος έργου και για να επαναφέρετε τα πακέτα NuGet που χρησιμοποιούνται στο δείγμα έργου.

Απαιτήσεις

  • Η λύση Dynamics 365 Field Service πρέπει να εγκατασταθεί στην παρουσία Dynamics 365 (online) (online). Περισσότερες πληροφορίες: Εγκατάσταση και ρύθμιση του Field Service

  • Δώστε το δικό σας κλειδί Google API στο αρχείο GoogleDataContracts.cs στο δείγμα:

    public const string GoogleApiKey = "<PROVIDE YOUR GOOGLE API KEY";

Παρουσιάζει

Αυτό το δείγμα δείχνει πώς μπορείτε να δημιουργήσετε μια προσαρμοσμένη προσθήκη για τις ενέργειες msdyn_GeocodeAddress και msdyn_RetrieveDistanceMatrix στο Universal Resource Scheduling για χρήση του Google Maps API για γεω-χωρικά δεδομένα, αντί να χρησιμοποιήσετε το προεπιλεγμένο AP των Χαρτών Bing.

Εκτέλεση δείγματος

Αυτό το δείγμα δημιουργεί ένα αρχείο συγκροτήσεων προσθήκης: CustomPlugin-FS-Geospatial.dll.

  1. Πραγματοποιήστε λήψη ή κλωνοποιήστε το αποθετήριο δειγμάτων.
  2. Μεταβείτε στη θέση όπου έχετε κάνει λήψη ή κλωνοποίηση του αποθετηρίου στον υπολογιστή σας, μεταβείτε στο φάκελο field-service/CustomPlugin-FS-Geosial και κάντε διπλό κλικ στο αρχείο CustomPlugin-FS-Geosial.sln για να ανοίξετε τη λύση στο Visual Studio.
  3. Στο Visual Studio, επιλέξτε Δόμηση>Δόμηση λύσης. Τα πακέτα NuGet που χρησιμοποιούνται στη λύση θα ληφθούν αυτόματα αν η επιλογή για επαναφορά των πακέτων NuGet αυτόματα κατά τη δημιουργία ενός έργου ενεργοποιείται στο Visual Studio. Περισσότερες πληροφορίες: Ενεργοποίηση και απενεργοποίηση επαναφοράς πακέτου

Μετά την εκτέλεση του δείγματος

Αφού εκτελέσετε με επιτυχία (δημιουργία) το δείγμα, ένα προσαρμοσμένο αρχείο συγκρότησης προσθήκης, FS-CustomPlugin-Geospatial.dll, θα είναι διαθέσιμο στον <Project>\bin\debug φάκελο. Καταχωρίστε την προσαρμοσμένη συγκρότηση προσθήκης στην παρουσία Dynamics 365 (online) (online) για να μπορέσετε να χρησιμοποιήσετε την προσθήκη για να χρησιμοποιήσετε το API του Google Maps αντί για το προεπιλεγμένο API Χαρτών Bing. Περισσότερες πληροφορίες: Καταχώριση και ανάπτυξη προσαρμοσμένης προσθήκης.

Δείγμα κώδικα προσθήκης για ενέργεια msdyn_GeocodeAddress

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

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());
        }
    }
}

Δήλωση περί προστασίας προσωπικών δεδομένων

Μπορείτε να χρησιμοποιήσετε το δείγμα κώδικα για αλληλεπίδραση με υπηρεσίες τρίτων των οποίων οι πρακτικές προστασίας προσωπικών δεδομένων και ασφάλειας ενδέχεται να διαφέρουν από αυτές των Microsoft Dynamics 365. ΑΝ ΥΠΟΒΑΛΕΤΕ ΔΕΔΟΜΕΝΑ ΣΕ ΥΠΗΡΕΣΙΕΣ ΤΡΙΤΩΝ, ΤΑ ΕΝ ΛΟΓΩ ΔΕΔΟΜΕΝΑ ΔΙΕΠΟΝΤΑΙ ΑΠΟ ΤΙΣ ΑΝΤΙΣΤΟΙΧΕΣ ΔΗΛΩΣΕΙΣ ΠΡΟΣΤΑΣΙΑΣ ΠΡΟΣΩΠΙΚΩΝ ΔΕΔΟΜΕΝΩΝ. Για την αποφυγή αμφιβολιών, η κοινή χρήση δεδομένων εκτός του Microsoft Dynamics 365 δεν καλύπτεται από τις συμβάσεις χρηστών του Microsoft Dynamics 365 ή του Κέντρου αξιοπιστίας του Microsoft Dynamics 365. Σας συνιστούμε να διαβάσετε τις συγκεκριμένες δηλώσεις προστασίας προσωπικών δεδομένων.

Δείτε επίσης

Δημιουργία προσαρμοσμένης προσθήκης για χρήση της προτιμώμενης υπηρεσίας παροχής γεω-χωρικών δεδομένων

Εγραφή και ανάπτυξη προσαρμοσμένης προσθήκης για χρήση της προτιμώμενης υπηρεσίας παροχής γεω-χωρικών δεδομένων