Jaa


Mukautetun laajennuksen luominen käyttämään ensisijaista geospatiaalista tietopalvelua

Tässä artikkelissa on tietoja kahdesta geospatiaalisesta toiminnosta Universal Resource Schedulingissa ja mukautetun laajennuksen luomisesta kahdelle geospatiaaliselle toiminnolle. Artikkeli sisältää myös esimerkkejä mukautetusta näytelaajennuksesta käyttämällä Google Mapsin ohjelmointirajapintaa geospatiaalisissa tiedoissa.

Geospatiaalisten toimintojen syöte- ja tulosparametrit

Mukautettua laajennusta kirjoitettaessa geospatiaalisten toimintojen syöte- ja tulosparametrit on otettava huomioon, jotta tiedät, mitä tietoja välitetään sisään ja mitä tulostustietoja odotetaan laajennuskoodissa.

Kahden geospatiaalisen toiminnan syöte- ja tulosparametreja voi tarkastella kahdella tavalla:

  • Verkon ohjelmointirajapinnan toiminnon viitesisältö: Universal Resource Schedulingin geospatiaalisten toimintojen viitetietojen tarkasteleminen.
    • Microsoft.Dynamics.CRM.msdyn_RetrieveDistanceMatrix
  • Toiminnon määritelmä: tarkastelemalla Dynamics 365:n toiminnon määritelmää saat tietoja syöte-ja tulosparametreista ja siitä, onko parametri pakollinen vai valinnainen.

Muistiinpano

Tässä artikkelissa/taulukossa mainitut WWW-ohjelmointirajapinnan tyypit ja toiminnot ovat käytettävissä ympäristössäsi, ja voit tutkia näitä tyyppejä ja toimintoja ympäristön palveluasiakirjan tai Postmanin avulla. Lisätietoja: WWW-ohjelmointirajapinnan palveluasiakirjat ja Postmanin käyttäminen Microsoft Dataversen WWW-ohjelmointirajapinnan kanssa.

Voit tarkastella toiminnon määritelmää valitsemalla Asetukset>Prosessit. Etsi seuraavaksi toiminnon nimi: Resurssien aikataulutus – maantieteellisten koordinaattien osoite tai Resurssien aikataulutus – etäisyysmatriisin noutaminen. Valitse sitten ruudukossa toiminto, jolloin sen määritelmä avautuu. Tämä on esimerkiksi Resurssien aikataulutus – maantieteellisten koordinaattien osoite (msdyn_GeocodeAddress) -toiminnon määritelmä, jossa korostettu alue sisältää tietoja syöte- ja tulosparametreista:

Action definition.

Mukautetun laajennuksen kirjoittaminen

Laajennukset ovat mukautettuja luokkia, jotka toteuttavat IPlugin-liittymän. Lisätietoja laajennuksen luomisesta on kohdassa Laajennuskehitys

Viitteenä on mukautettu näytelaajennus, joka näyttää, miten Google Mapsin ohjelmointirajapintaa voi käyttää tuottamaan geospatiaalisia tietoja kenttätoimintoja varten oletusarvoisen Bing Mapsin ohjelmointirajapinnan sijaan. Lisätietoja: Näyte: Mukautettu laajennus Google Maps -ohjelmointirajapinnan käyttämiseen geospatiaalisena tietopalveluna.

Seuraava kummankin näytelaajennuksen koodi käyttää Googlen ohjelmointirajapinnan tietoja:

ExecuteGeocodeAddress-menetelmä msdyn_GeocodeAddress.cs-laajennustiedostossa

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

}

ExecuteDistanceMatrix-menetelmä msdyn_RetrieveDistanceMatrix.cs-laajennustiedostossa

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

Kun olet kirjoittanut mukautetun laajennuskoodin, muodosta projekti laajennuskokoonpanon (.dll) luontia varten, sillä tarvitse sitä laajennuksen rekisteröimiseen Universal Resource Schedulingin geospatiaalisiin toimintoihin.