Deli z drugimi prek


Delo z rešitvami ob uporabi razvojnega kompleta SDK okolja Dataverse

Kot del življenjskega cikla od razvoja do produkcije boste morda želeli ustvariti avtomatizacijo po meri za obravnavo določenih nalog. Tako lahko na primer pri razvoju projekta DevOps izvedete kodo ali skript po meri, ki ustvari preizkusno okolje, uvozi neupravljano rešitev, izvozi to neupravljano rešitev kot upravljano rešitev in na koncu izbriše okolje. Te in še več operacij lahko izvedete z uporabo API-jev, ki so vam na voljo. Tukaj je nekaj primerov, kaj lahko dosežete z uporabo kompleta SDK za .NET in kode po meri. Dataverse

opomba,

Te postopke lahko izvedete tudi prek spletnega API-ja. Povezana dejanja so: ImportSolution, ExportSolution, CloneAsPatch in CloneAsSolution.

Vzorci kode v tem članku uporabljajo zgodnje vezane tipe entitet, ustvarjene s CrmSvcUtil ali PAC CLI. Več informacij: Pozno vezano in zgodnje vezano programiranje z uporabo SDK-ja za .NET

Ustvarjanje, izvoz ali uvoz neupravljane rešitve

Naučite se izvajati nekatere pogoste operacije reševanja s kodo C#. Če si želite ogledati celoten delujoč primer kode C#, ki prikazuje te vrste operacij z rešitvami in še več, pojdite na Vzorec: Delo z rešitvami.

Ustvarjanje izdajatelja

Vsaka rešitev zahteva založnika, ki ga predstavlja tabela Založnik. ... Založnik zahteva te lastnosti:

  • Predpono prilagoditve
  • Enolično ime
  • Prijazno ime

opomba,

Za zdrav pristop k upravljanju življenjskega cikla aplikacij (ALM) za uvajanje prilagoditev vedno uporabite založnika in rešitev po meri, ne pa privzete rešitve in založnika.

Naslednji primer kode najprej definira založnika in nato na podlagi enoličnega imena preveri, ali založnik že obstaja. Če že obstaja, je bila predpona prilagajanja morda spremenjena. Ta vzorec poskuša zajeti trenutno predpono prilagoditve. Zajame se tudi PublisherId, tako da je zapis izdajatelja mogoče izbrisati. Če izdajatelja ni mogoče najti, se na podlagi metode IOrganizationService.Create.Create ustvari nov izdajatelj.

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

Ustvarjanje neupravljane rešitve

Ko imate na voljo izdajatelja po meri, lahko ustvarite neupravljano rešitev. V naslednji tabeli so navedeni stolpci z opisi, ki jih vsebuje rešitev.

Oznaka stolpca Opis
Prikazano ime Ime rešitve.
Imenu Microsoft Dataverse ustvari enolično ime na podlagi prikaznega imena. Enolično ime lahko uredite. Enolično ime je lahko sestavljeno le iz alfanumeričnih znakov ali podčrtaja.
Izdajatelj Uporabite iskanje Izdajatelj, da povežete rešitev z izdajateljem.
Različica Različico določite v obliki: večja.manjša.gradnja.revizija, na primer 1.0.0.0.
Stran s konfiguracijo Če v svojo rešitev vključite spletni vir HTML, ga lahko prek tega iskanja dodate kot namensko stran s konfiguracijo rešitve.
Description V ta stolpec vključite vse pomembne podrobnosti o svoji rešitvi.

Tukaj je vzorčna koda za ustvarjanje neupravljane rešitve, ki uporablja založnika, ki smo ga ustvarili v prejšnjem razdelku.

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

Ko ustvarite neupravljano rešitev, dodajte komponente rešitve tako, da jih ustvarite v kontekstu te rešitve ali pa dodate obstoječe komponente iz drugih rešitev. Več informacij: Dodajanje nove komponente rešitve in Dodajanje obstoječe komponente rešitve

Izvoz neupravljane rešitve

Ta vzorec kode ponazarja, kako izvoziti neupravljano rešitev ali zapakirati upravljano rešitev. Koda uporablja razred ExportSolutionRequest za izvoz stisnjene datoteke, ki predstavlja neupravljano rešitev. Možnost za ustvarjanje upravljane rešitve se nastavi z uporabo lastnosti Managed. Ta vzorec shrani datoteko samplesolution.zip v izhodno mapo.

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

Uvoz neupravljane rešitve

Uvoz (ali nadgradnja) rešitve prek kode se izvede na podlagi ImportSolutionRequest.

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

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

_serviceProxy.Execute(impSolReq);

Spremljanje uspešnosti uvoza

Tabelo ImportJob lahko uporabite za zajemanje podatkov o uspešnosti uvoza rešitve. Ko za zahtevo ImportSolutionRequest določite vrednost ImportJobId , lahko to vrednost uporabite za poizvedbo v tabeli o stanju uvoza.ImportJob ImportJobId lahko uporabite tudi za prenos datoteke z dnevnikom uvoza prek sporočila RetrieveFormattedImportJobResultsRequest.

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

Vsebina lastnosti Data je niz, ki predstavlja datoteko XML rešitve.

Dodajanje in odstranjevanje komponent rešitve

Preberite, kako lahko s kodo dodate in odstranite komponente rešitve.

Dodajanje nove komponente rešitve

Ta primer prikazuje, kako ustvarite komponento rešitve, ki je povezana z določeno rešitvijo. Če komponente rešitve ob njenem ustvarjanju ne povežete z določeno rešitvijo, bo dodana le privzeti rešitvi, ki jo morate dodati v rešitev ročno ali z uporabo kode, ki je vključena v razdelku Dodajanje obstoječe komponente rešitve. ...

Ta koda ustvari nov globalni nabor možnosti in ga doda v rešitev z enoličnim imenom, ki je enako _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);

Dodajanje obstoječe komponente rešitve

Ta primer prikazuje, kako dodate obstoječo komponento rešitve v rešitev.

Naslednja koda uporablja AddSolutionComponentRequest za dodajanje tabele Account kot komponente rešitve v neupravljano rešitev.

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

Odstranjevanje komponente rešitve

Ta primer prikazuje, kako odstranite komponento rešitve iz neupravljane rešitve. Naslednja koda uporablja RemoveSolutionComponentRequest za odstranitev komponente rešitve tabele iz neupravljane rešitve. solution.UniqueName se sklicuje na rešitev, ustvarjeno v koraku Ustvarjanje neupravljane rešitve.

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

Brisanje rešitve

Spodnji primer prikazuje, kako pridobite rešitev z uporabo uniquename rešitve in nato izvlečete solutionid iz rezultatov. Primer nato uporabi solutionid s IOrganizationService. Delete način za brisanje rešitve.

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

Kloniranje, popravki in nadgradnja

Dodatne postopke rešitve lahko izvedete z API-ji, ki so na voljo. Za rešitve za kloniranje in nameščanje popravkov uporabite CloneAsPatchRequest in CloneAsSolutionRequest. Za informacije o kloniranju in popravkih glejte Ustvarjanje popravkov rešitev.

Pri nadgradnjah rešitve uporabite StageAndUpgradeRequest in DeleteAndPromoteRequest. Za več informacij o postopku priprave in nadgradenj glejte Nadgradnja ali posodobitev rešitve.

Iskanje odvisnosti rešitve

Ta primer prikazuje, kako ustvarite poročilo, ki prikazuje odvisnosti med komponentami rešitve.

Ta koda izvaja te operacije:

  • pridobi vse komponente za rešitev.

  • pridobi vse odvisnosti za vsako komponento.

  • prikaže poročilo, ki opisuje odvisnost, za vsako najdeno odvisnost.

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

Metoda DependencyReport je v spodnjem primeru kode.

Poročilo o odvisnosti

Metoda DependencyReport ponuja prijaznejše sporočilo na podlagi informacij, najdenih v odvisnosti.

opomba,

V tem primeru je metoda uporabljena le deloma. Sporočila lahko prikaže samo za komponente rešitve atributa in nabora možnosti.

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

Zaznavanje, ali je mogoče komponento rešitve izbrisati

Uporabite sporočilo RetrieveDependenciesForDeleteRequest, da poiščete druge komponente rešitve, ki bi preprečile brisanje določene komponente rešitve. Naslednji primer kode išče atribute z uporabo znanega stolpca globalne izbire. Vsak atribut, ki uporablja globalno izbiro, bi preprečil brisanje globalne izbire.

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