Ratkaisujen käsitteleminen Dataverse SDK:ta käyttäen

Kehityksen ja tuotannon elinkaaren osana haluat ehkä luoda mukautettua automaatiota tiettyjen tehtävien käsittelyyn. Esimerkiksi DevOps-projektiputkessa voi suorittaa mukautetun koodin tai komentosarjan, joka luo eristysympäristön, tuo hallitsemattoman ratkaisun, vie kyseisen ratkaisun hallittuna ratkaisuna ja lopulta poistaa ympäristön. Voit tehdä tämän ja paljon muuta käyttämällä käytettävissä olevia ohjelmointirajapintoja. Seuraavassa on esimerkkejä siitä, mitä voit tehdä .NET:n Dataverse SDK:n ja mukautetun koodin avulla.

Muistiinpano

Voit myös suorittaa nämä samat toiminnot käyttämällä verkon ohjelmointirajapintaa. Liittyvät toiminnot ovat: ImportSolution, ExportSolution, CloneAsPatch ja CloneAsSolution.

Hallitsemattoman ratkaisun luominen, vieminen tai tuominen

Katsotaan, miten joitakin yleisiä ratkaisutoimintoja suoritetaan C#-koodin avulla. Jos haluat katsoa täydellisen käsittelyn C#-koodiesimerkin, joka esittelee esimerkiksi tämäntyyppiset ratkaisutoiminnot, katso Esimerkki: Ratkaisujen käsitteleminen.

Julkaisijan luominen

Jokainen ratkaisu edellyttää julkaisijan, jota edustaa julkaisijan entiteetti. Seuraavassa kerrotaan julkaisijan edellytykset:

  • Mukautuksen etuliite
  • Yksilöivä nimi
  • Kutsumanimi

Muistiinpano

Toimivassa ALM-lähestymistavassa käytetään aina mukautusten käyttöönotossa mukautettua julkaisijaa ja ratkaisua, ei oletusratkaisua ja -julkaisijaa.

Seuraava koodiesimerkki määrittää ensin julkaisijan ja tarkistaa sitten, onko julkaisija jo olemassa yksilöllisen nimen perusteella. Jos se on jo olemassa, mukauttamisen etuliite on ehkä muuttunut, joten tässä esimerkissä on tarkoitus siepata nykyinen mukautuksen etuliite. Koska PublisherId on myös siepattu, julkaisijatietue voidaan poistaa. Jos julkaisijaa ei löydy, uusi julkaisija luodaan käyttämälläIOrganizationService.Create- menetelmää.

// Define a new publisher
Publisher _myPublisher = new Publisher
{
   UniqueName = "contoso-publisher",
   FriendlyName = "Contoso publisher",
   SupportingWebsiteUrl =
      "https://learn.microsoft.com/powerapps/developer/data-platform/overview",
   CustomizationPrefix = "contoso",
   EMailAddress = "someone@contoso.com",
   Description = "This publisher was created from sample code"
};

// Does the publisher already exist?
QueryExpression querySamplePublisher = new QueryExpression
{
   EntityName = Publisher.EntityLogicalName,
   ColumnSet = new ColumnSet("publisherid", "customizationprefix"),
   Criteria = new FilterExpression()
};

querySamplePublisher.Criteria.AddCondition("uniquename", ConditionOperator.Equal,
   _myPublisher.UniqueName);

EntityCollection querySamplePublisherResults =
   _serviceProxy.RetrieveMultiple(querySamplePublisher);

Publisher SamplePublisherResults = null;

// If the publisher already exists, use it
if (querySamplePublisherResults.Entities.Count > 0)
{
   SamplePublisherResults = (Publisher)querySamplePublisherResults.Entities[0];
   _publisherId = (Guid)SamplePublisherResults.PublisherId;
   _customizationPrefix = SamplePublisherResults.CustomizationPrefix;
}

// If the publisher doesn't exist, create it
if (SamplePublisherResults == null)
{
   _publisherId = _serviceProxy.Create(_myPublisher);

   Console.WriteLine(String.Format("Created publisher: {0}.",
   _myPublisher.FriendlyName));

   _customizationPrefix = _myPublisher.CustomizationPrefix;
}

Ei-hallitun ratkaisun luominen

Kun mukautettu julkaisija on käytettävissä, voit luoda hallitsemattoman ratkaisun. Seuraavassa taulukossa on luettelo kentistä, joiden kuvaukset ratkaisu sisältää.

Kentän teksti Kuvaus
Näyttönimi Ratkaisun nimi.
Nimi Microsoft Dataverse luo yksilöivän nimen näyttönimen perusteella. Voit muokata yksilöivää nimeä. Yksilöivässä nimessä saa olla vain aakkosnumeerisia merkkejä tai alaviivoja.
Julkaisija Käytä Julkaisijan hakua, jos haluat liittää ratkaisun julkaisijaan.
Versio Määritä versio käyttämällä seuraavaa muotoa: major.minor.build.revision (esimerkiksi 1.0.0.0.
Määrityssivu Jos sisällytät ratkaisuun HTML-verkkoresurssin, voit lisätä sen ratkaisun määrityssivuksi tämän haun avulla.
Kuvaus Tämän kentän avulla voit lisätä ratkaisun olennaiset tiedot.

Alla on esimerkkikoodi. Sen avulla voit luoda hallitsemattoman ratkaisun, joka käyttää edellisessä osassa luomaasi julkaisijaa.

// Create a solution
Solution solution = new Solution
{
   UniqueName = "sample-solution",
   FriendlyName = "Sample solution",
   PublisherId = new EntityReference(Publisher.EntityLogicalName, _publisherId),
   Description = "This solution was created by sample code.",
   Version = "1.0"
};

// Check whether the solution already exists
QueryExpression queryCheckForSampleSolution = new QueryExpression
{
   EntityName = Solution.EntityLogicalName,
   ColumnSet = new ColumnSet(),
   Criteria = new FilterExpression()
};

queryCheckForSampleSolution.Criteria.AddCondition("uniquename",
   ConditionOperator.Equal, solution.UniqueName);

// Attempt to retrieve the solution
EntityCollection querySampleSolutionResults =
   _serviceProxy.RetrieveMultiple(queryCheckForSampleSolution);

// Create the solution if it doesn't already exist
Solution SampleSolutionResults = null;

if (querySampleSolutionResults.Entities.Count > 0)
{
   SampleSolutionResults = (Solution)querySampleSolutionResults.Entities[0];
   _solutionsSampleSolutionId = (Guid)SampleSolutionResults.SolutionId;
}

if (SampleSolutionResults == null)
{
   _solutionsSampleSolutionId = _serviceProxy.Create(solution);
}

Kun olet luonut hallitsemattoman ratkaisun, voit lisätä ratkaisun komponentteja luomalla ne tämän ratkaisun kontekstissa tai lisäämällä aiemmin luotuja komponentteja muista ratkaisuista. Lisätietoja: Uuden ratkaisun komponentin lisääminen ja Aiemmin luodun ratkaisun komponentin lisääminen

Hallitsemattoman ratkaisun vieminen

Tässä koodiesimerkissä näytetään, miten hallitsemattoman ratkaisun voi viedä tai miten hallitun ratkaisun voi pakata. Koodi käyttää luokkaa ExportSolutionRequest hallitsematonta ratkaisua edustavan pakatun tiedoston viemisessä. Hallitun ratkaisun luontivaihtoehto määritetäänHallittu -ominaisuuden avulla. Tässä esimerkissä tallennetaan tiedosto, jonka nimi on samplesolution.zip tuloskansioon.

// Export a solution
ExportSolutionRequest exportSolutionRequest = new ExportSolutionRequest();
exportSolutionRequest.Managed = false;
exportSolutionRequest.SolutionName = solution.UniqueName;

ExportSolutionResponse exportSolutionResponse =
   (ExportSolutionResponse)_serviceProxy.Execute(exportSolutionRequest);

byte[] exportXml = exportSolutionResponse.ExportSolutionFile;
string filename = solution.UniqueName + ".zip";

File.WriteAllBytes(outputDir + filename, exportXml);

Console.WriteLine("Solution exported to {0}.", outputDir + filename);

Hallitsemattoman ratkaisun tuominen

Ratkaisun tuominen (tai päivittäminen) koodin avulla tapahtuu käyttämällä kohdetta ImportSolutionRequest.

// Install or upgrade a solution
byte[] fileBytes = File.ReadAllBytes(ManagedSolutionLocation);

ImportSolutionRequest impSolReq = new ImportSolutionRequest()
{
   CustomizationFile = fileBytes
};

_serviceProxy.Execute(impSolReq);

Tuonnin onnistumisen seuraaminen

ImportJob-entiteetin avulla voit siepata ratkaisun tuomisen onnistumista koskevat tiedot. Kun määrität ImportJobId-entiteetin ImportSolutionRequest-pyynnölle, voit käyttää tätä arvoa tehdessäsi kyselyn ImportJob-entiteetistä ja tuonnin tilasta. ImportJobId-entiteettiä voidaan käyttää myös tuontilokitiedoston lataamisessa käyttämällä RetrieveFormattedImportJobResultsRequest-viestiä.

// Monitor solution import success
byte[] fileBytesWithMonitoring = File.ReadAllBytes(ManagedSolutionLocation);

ImportSolutionRequest impSolReqWithMonitoring = new ImportSolutionRequest()
{
   CustomizationFile = fileBytes,
   ImportJobId = Guid.NewGuid()
};

_serviceProxy.Execute(impSolReqWithMonitoring);

ImportJob job = (ImportJob)_serviceProxy.Retrieve(ImportJob.EntityLogicalName,
   impSolReqWithMonitoring.ImportJobId, new ColumnSet(new System.String[] { "data",
   "solutionname" }));

System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
doc.LoadXml(job.Data);

String ImportedSolutionName =
   doc.SelectSingleNode("//solutionManifest/UniqueName").InnerText;

String SolutionImportResult =
   doc.SelectSingleNode("//solutionManifest/result/\@result").Value;

Console.WriteLine("Report from the ImportJob data");

Console.WriteLine("Solution Unique name: {0}", ImportedSolutionName);

Console.WriteLine("Solution Import Result: {0}", SolutionImportResult);

Console.WriteLine("");

// This code displays the results for Global Option sets installed as part of a
// solution.

System.Xml.XmlNodeList optionSets = doc.SelectNodes("//optionSets/optionSet");

foreach (System.Xml.XmlNode node in optionSets)
{
   string OptionSetName = node.Attributes["LocalizedName"].Value;
   string result = node.FirstChild.Attributes["result"].Value;

   if (result == "success")
   {
      Console.WriteLine("{0} result: {1}",OptionSetName, result);
   }
   else
   {
      string errorCode = node.FirstChild.Attributes["errorcode"].Value;
      string errorText = node.FirstChild.Attributes["errortext"].Value;

      Console.WriteLine("{0} result: {1} Code: {2} Description: {3}",OptionSetName,
      result, errorCode, errorText);
   }
}

Data-ominaisuuden sisältö on merkkijono, joka vastaa ratkaisun XML-tiedostoa.

Ratkaisun osien lisääminen tai poistaminen järjestelmästä

Tietoja ratkaisun osien lisäämisestä ja poistamisesta koodin avulla.

Uusien osien lisääminen ratkaisuun

Tässä esimerkissä kuvataan, miten tiettyyn ratkaisuun liittyvä ratkaisun osa luodaan. Jos et liitä ratkaisun osaa tiettyyn ratkaisuun, kun se on luotu, se lisätään vain oletusratkaisuun, ja se on lisättävä ratkaisuun manuaalisesti tai käyttämällä aiemmin luodun ratkaisun osan Lisää-koodia.

Tämä koodi luo uuden yleisen asetusjoukon ja lisää sen ratkaisuun, jonka yksilöivä nimi on sama kuin _primarySolutionName.

OptionSetMetadata optionSetMetadata = new OptionSetMetadata()
{
   Name = _globalOptionSetName,
   DisplayName = new Label("Example Option Set", _languageCode),
   IsGlobal = true,
   OptionSetType = OptionSetType.Picklist,
   Options =
{
   new OptionMetadata(new Label("Option 1", _languageCode), 1),
   new OptionMetadata(new Label("Option 2", _languageCode), 2)
}
};
CreateOptionSetRequest createOptionSetRequest = new CreateOptionSetRequest
{
   OptionSet = optionSetMetadata                
};

createOptionSetRequest.SolutionUniqueName = _primarySolutionName;
_serviceProxy.Execute(createOptionSetRequest);

Aiemmin luodun ratkaisun osan lisääminen

Tässä esimerkissä kuvataan, miten aiemmin luotu ratkaisun osa lisätään ratkaisuun.

Seuraava koodi käyttää AddSolutionComponentRequest -kohdetta, kun haluat lisätä Account-entiteetin ratkaisun komponentiksi hallitsemattomaan ratkaisuun.

// Add an existing Solution Component
// Add the Account entity to the solution
RetrieveEntityRequest retrieveForAddAccountRequest = new RetrieveEntityRequest()
{
   LogicalName = Account.EntityLogicalName
};
RetrieveEntityResponse retrieveForAddAccountResponse = (RetrieveEntityResponse)_serviceProxy.Execute(retrieveForAddAccountRequest);
AddSolutionComponentRequest addReq = new AddSolutionComponentRequest()
{
   ComponentType = (int)componenttype.Entity,
   ComponentId = (Guid)retrieveForAddAccountResponse.EntityMetadata.MetadataId,
   SolutionUniqueName = solution.UniqueName
};
_serviceProxy.Execute(addReq);

Poista ratkaisun osa

Tässä esimerkissä kuvataan, miten aiemmin luotu ratkaisun osa poistetaan hallitsemattomasta ratkaisusta. Seuraava koodi käyttää RemoveSolutionComponentRequest -kohdetta, kun haluat poistaa entiteetin ratkaisun komponentin hallitsemattomasta ratkaisusta. solution.UniqueName viittaa ratkaisun luomiseen, joka luotiin hallitsemattomassa ratkaisussa.

// Remove a Solution Component
// Remove the Account entity from the solution
RetrieveEntityRequest retrieveForRemoveAccountRequest = new RetrieveEntityRequest()
{
   LogicalName = Account.EntityLogicalName
};
RetrieveEntityResponse retrieveForRemoveAccountResponse = (RetrieveEntityResponse)_serviceProxy.Execute(retrieveForRemoveAccountRequest);

RemoveSolutionComponentRequest removeReq = new RemoveSolutionComponentRequest()
{
   ComponentId = (Guid)retrieveForRemoveAccountResponse.EntityMetadata.MetadataId,
   ComponentType = (int)componenttype.Entity,
   SolutionUniqueName = solution.UniqueName
};
_serviceProxy.Execute(removeReq);

Ratkaisun poistaminen

Seuraavasta esimerkistä käy ilmi, miten ratkaisun voi noutaa uniquename-ratkaisun avulla ja poimia sitten solutionid-kohteen tuloksista. Näyte käyttää sitten solutionid-kenttää kohteen IOrganizationService kanssa. Delete-menetelmä ratkaisun poistamiseen.

// Delete a solution

QueryExpression queryImportedSolution = new QueryExpression
{
    EntityName = Solution.EntityLogicalName,
    ColumnSet = new ColumnSet(new string[] { "solutionid", "friendlyname" }),
    Criteria = new FilterExpression()
};


queryImportedSolution.Criteria.AddCondition("uniquename", ConditionOperator.Equal, ImportedSolutionName);

Solution ImportedSolution = (Solution)_serviceProxy.RetrieveMultiple(queryImportedSolution).Entities[0];

_serviceProxy.Delete(Solution.EntityLogicalName, (Guid)ImportedSolution.SolutionId);

Console.WriteLine("Deleted the {0} solution.", ImportedSolution.FriendlyName);

Kloonaus, korjaaminen ja päivittäminen

Voit suorittaa muita ratkaisun toimintoja käytettävissä olevien ohjelmointirajapintojen avulla. Kloonaus- ja paikkausratkaisut käyttävät CloneAsPatchRequest- ja CloneAsSolutionRequest -pyyntöjä. Tietoja kloonauksesta ja korjaustiedostoista on kohdassa Ratkaisun korjaustiedostojen luominen.

Kun suoritat ratkaisun päivityksiä, käytä StageAndUpgradeRequest- ja DeleteAndPromoteRequest -versioita. Lisätietoja valmisteluprosessista ja päivityksistä on aiheessa ratkaisun päivittäminen.

Ratkaisujen riippuvuuksien havaitseminen

Tässä esimerkissä luodaan raportti, jossa näkyvät ratkaisun osien riippuvuudet.

Tämä koodi:

  • Hae ratkaisun kaikki osat.

  • Nouda kunkin osan kaikki riippuvuudet.

  • Näytä jokaiselle löydetylle riippuvuudelle raportti, joka kuvaa riippuvuutta.

// Grab all Solution Components for a solution.
QueryByAttribute componentQuery = new QueryByAttribute
{
    EntityName = SolutionComponent.EntityLogicalName,
    ColumnSet = new ColumnSet("componenttype", "objectid", "solutioncomponentid", "solutionid"),
    Attributes = { "solutionid" },

    // In your code, this value would probably come from another query.
    Values = { _primarySolutionId }
};

IEnumerable<SolutionComponent> allComponents =
    _serviceProxy.RetrieveMultiple(componentQuery).Entities.Cast<SolutionComponent>();

foreach (SolutionComponent component in allComponents)
{
    // For each solution component, retrieve all dependencies for the component.
    RetrieveDependentComponentsRequest dependentComponentsRequest =
        new RetrieveDependentComponentsRequest
        {
            ComponentType = component.ComponentType.Value,
            ObjectId = component.ObjectId.Value
        };
    RetrieveDependentComponentsResponse dependentComponentsResponse =
        (RetrieveDependentComponentsResponse)_serviceProxy.Execute(dependentComponentsRequest);

    // If there are no dependent components, we can ignore this component.
    if (dependentComponentsResponse.EntityCollection.Entities.Any() == false)
        continue;

    // If there are dependencies upon this solution component, and the solution
    // itself is managed, then you will be unable to delete the solution.
    Console.WriteLine("Found {0} dependencies for Component {1} of type {2}",
        dependentComponentsResponse.EntityCollection.Entities.Count,
        component.ObjectId.Value,
        component.ComponentType.Value
        );
    //A more complete report requires more code
    foreach (Dependency d in dependentComponentsResponse.EntityCollection.Entities)
    {
        DependencyReport(d);
    }
}

DependencyReport-menetelmä on seuraavassa koodinäytteessä.

Riippuvuusraportti

DependencyReport-menetelmä tarjoaa ystävällisemmän viestin, joka perustuu riippuvuuden sisällä oleviin tietoihin.

Muistiinpano

Tässä esimerkissä menetelmä on toteutettu vain osittain. Se voi näyttää vain määrite- ja asetusjoukkoratkaisun osien sanomia.

/// <summary>
/// Shows how to get a more friendly message based on information within the dependency
/// <param name="dependency">A Dependency returned from the RetrieveDependentComponents message</param>
/// </summary> 
public void DependencyReport(Dependency dependency)
{
 // These strings represent parameters for the message.
    String dependentComponentName = "";
    String dependentComponentTypeName = "";
    String dependentComponentSolutionName = "";
    String requiredComponentName = "";
    String requiredComponentTypeName = "";
    String requiredComponentSolutionName = "";

 // The ComponentType global Option Set contains options for each possible component.
    RetrieveOptionSetRequest componentTypeRequest = new RetrieveOptionSetRequest
    {
     Name = "componenttype"
    };

    RetrieveOptionSetResponse componentTypeResponse = (RetrieveOptionSetResponse)_serviceProxy.Execute(componentTypeRequest);
    OptionSetMetadata componentTypeOptionSet = (OptionSetMetadata)componentTypeResponse.OptionSetMetadata;
 // Match the Component type with the option value and get the label value of the option.
    foreach (OptionMetadata opt in componentTypeOptionSet.Options)
    {
     if (dependency.DependentComponentType.Value == opt.Value)
     {
      dependentComponentTypeName = opt.Label.UserLocalizedLabel.Label;
     }
     if (dependency.RequiredComponentType.Value == opt.Value)
     {
      requiredComponentTypeName = opt.Label.UserLocalizedLabel.Label;
     }
    }
 // The name or display name of the compoent is retrieved in different ways depending on the component type
    dependentComponentName = getComponentName(dependency.DependentComponentType.Value, (Guid)dependency.DependentComponentObjectId);
    requiredComponentName = getComponentName(dependency.RequiredComponentType.Value, (Guid)dependency.RequiredComponentObjectId);

 // Retrieve the friendly name for the dependent solution.
    Solution dependentSolution = (Solution)_serviceProxy.Retrieve
     (
      Solution.EntityLogicalName,
      (Guid)dependency.DependentComponentBaseSolutionId,
      new ColumnSet("friendlyname")
     );
    dependentComponentSolutionName = dependentSolution.FriendlyName;
    
 // Retrieve the friendly name for the required solution.
    Solution requiredSolution = (Solution)_serviceProxy.Retrieve
      (
       Solution.EntityLogicalName,
       (Guid)dependency.RequiredComponentBaseSolutionId,
       new ColumnSet("friendlyname")
      );
    requiredComponentSolutionName = requiredSolution.FriendlyName;

 // Display the message
     Console.WriteLine("The {0} {1} in the {2} depends on the {3} {4} in the {5} solution.",
     dependentComponentName,
     dependentComponentTypeName,
     dependentComponentSolutionName,
     requiredComponentName,
     requiredComponentTypeName,
     requiredComponentSolutionName);
}

Tunnista, voidaanko ratkaisukomponentti poistaa

Käytä RetrieveDependenciesForDeleteRequest-sanomaa, kun haluat etsiä muita ratkaisun osia, jotka estävät tietyn ratkaisun osan poistamisen. Seuraava koodinäyte etsii määritteitä, jotka käyttävät tunnettua maailmanlaajuista asetusjoukkoa. Kaikki yleisen asetusjoukon käyttävät määritteet estävät yleisen asetusjoukon poistamisen.

// Use the RetrieveOptionSetRequest message to retrieve  
// a global option set by it's name.
RetrieveOptionSetRequest retrieveOptionSetRequest =
    new RetrieveOptionSetRequest
    {
     Name = _globalOptionSetName
    };

// Execute the request.
RetrieveOptionSetResponse retrieveOptionSetResponse =
    (RetrieveOptionSetResponse)_serviceProxy.Execute(
    retrieveOptionSetRequest);
_globalOptionSetId = retrieveOptionSetResponse.OptionSetMetadata.MetadataId;
if (_globalOptionSetId != null)
{ 
 // Use the global OptionSet MetadataId with the appropriate componenttype
 // to call RetrieveDependenciesForDeleteRequest
 RetrieveDependenciesForDeleteRequest retrieveDependenciesForDeleteRequest = new RetrieveDependenciesForDeleteRequest 
{ 
 ComponentType = (int)componenttype.OptionSet,
 ObjectId = (Guid)_globalOptionSetId
};

 RetrieveDependenciesForDeleteResponse retrieveDependenciesForDeleteResponse =
  (RetrieveDependenciesForDeleteResponse)_serviceProxy.Execute(retrieveDependenciesForDeleteRequest);
 Console.WriteLine("");
 foreach (Dependency d in retrieveDependenciesForDeleteResponse.EntityCollection.Entities)
 {

  if (d.DependentComponentType.Value == 2)//Just testing for Attributes
  {
   String attributeLabel = "";
   RetrieveAttributeRequest retrieveAttributeRequest = new RetrieveAttributeRequest
   {
    MetadataId = (Guid)d.DependentComponentObjectId
   };
   RetrieveAttributeResponse retrieveAttributeResponse = (RetrieveAttributeResponse)_serviceProxy.Execute(retrieveAttributeRequest);

   AttributeMetadata attmet = retrieveAttributeResponse.AttributeMetadata;

   attributeLabel = attmet.DisplayName.UserLocalizedLabel.Label;
  
    Console.WriteLine("An {0} named {1} will prevent deleting the {2} global option set.", 
   (componenttype)d.DependentComponentType.Value, 
   attributeLabel, 
   _globalOptionSetName);
  }
 }
}