Dijeli putem


Rad s rješenjima upotrebom SDK Dataverse

Kao dio životnog ciklusa razvoja do proizvodnje, možda ćete htjeti stvoriti prilagođenu automatizaciju za rukovanje određenim zadacima. Na primjer, na svom kanalu za DevOps projekt možda želite izvršiti prilagođeni kôd ili skriptu koja stvara testno okruženje, uvozi neupravljano rješenje, izvozi to neupravljano rješenje kao upravljano rješenje i, na kraju, briše okruženje. Ove i druge operacije možete izvršiti pomoću API-ja koji su vam dostupni. Evo nekoliko primjera što možete postići pomoću SDK-a Dataverse za .NET i prilagođenog koda.

Napomena

Te operacije možete izvršiti i upotrebom Web API-ja. Povezane radnje: Uvoz rješenja, Izvoz rješenja, Kloniranje kao zakrpa i Kloniranje kao rješenje.

Uzorci koda u ovom članku koriste rano vezane vrste entiteta generirane pomoću CrmSvcUtil ili PAC CLI-ja. Dodatne informacije: Kasno i rano vezano programiranje pomoću SDK-a za .NET

Stvaranje, izvoz ili uvoz neupravljanog rješenja

Saznajte kako izvesti neke uobičajene operacije rješenja pomoću C# koda. Da biste vidjeli cijeli radni primjer C# koda koji prikazuje ove vrste operacija rješenja i još mnogo toga, idite na Uzorak: Rad s rješenjima.

Stvaranje izdavača

Za svako rješenje potreban je izdavač koji je predstavljen tablicom Izdavač. Izdavaču su potrebna sljedeća svojstva:

  • Prefiks prilagođavanja
  • Jedinstveni naziv
  • Neslužbeni naziv

Napomena

Za zdrav pristup upravljanju životnim ciklusom aplikacije (ALM) uvijek koristite prilagođenog izdavača i rješenje, a ne zadano rješenje i izdavača, za implementaciju prilagodbi.

Sljedeći primjer koda najprije definira izdavača, a zatim provjerava postoji li izdavač već na temelju jedinstvenog naziva. Ako već postoji, prefiks prilagodbe možda je promijenjen. Ovaj uzorak nastoji obuhvatiti trenutni prefiks prilagodbe. Bilježi se i PublisherId pa se zapis izdavača može izbrisati. Ako izdavač nije pronađen, novi izdavač stvara se pomoću metode IOrganizationService.Create.

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

Stvaranje neupravljanog rješenja

Kada vam je dostupan prilagođeni izdavač, možete stvoriti neupravljano rješenje. U sljedećoj tablici navedeni su stupci s opisima koje rješenje sadrži.

Oznaka stupca Opis
Prikazni naziv Naziv rješenja.
Ime/naziv Microsoft Dataverse generira jedinstveni naziv na temelju zaslonskog naziva. Jedinstveni naziv možete urediti. Jedinstveni naziv smije sadržavati samo alfanumeričke znakove ili podvlaku.
Izdavač Koristite referentno polje Izdavač da biste rješenje povezali s izdavačem.
Verzija Navedite verziju pomoću format: major.minor.build.revision, na primjer, 1.0.0.0.
Konfiguracijska stranica Ako u svoje rješenje uključite HTML web-resurs, možete upotrijebiti referentno polje kako biste ga dodali kao svoju konfiguracijsku stranicu rješenja.
Opis Pomoću ovog stupca uključite sve relevantne pojedinosti o rješenju.

Evo primjera koda za stvaranje neupravljanog rješenja koje koristi izdavača kojeg smo stvorili u prethodnom odjeljku.

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

Nakon što stvorite neupravljano rješenje, dodajte komponente rješenja tako da ih stvorite u kontekstu ovog rješenja ili dodavanjem postojećih komponenti iz drugih rješenja. Dodatne informacije: Dodavanje nove komponente rješenja i Dodavanje postojeće komponente rješenja

Uvoz neupravljanog rješenja

Ovaj uzorak koda prikazuje kako izvesti neupravljano rješenje ili pakirati upravljano rješenje. Kôd koristi klasu ExportSolutionRequest za izvoz sažete datoteke koja predstavlja neupravljano rješenje. Mogućnost stvaranja upravljanog rješenja postavlja se pomoću svojstva Upravljano. Ovaj uzorak sprema datoteku naziva samplesolution.zip u izlaznu mapu.

// 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 neupravljanog rješenja

Uvoz (ili nadogradnja) rješenja pomoću koda vrši se pomoću zahtjeva za uvoz rješenja.

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

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

_serviceProxy.Execute(impSolReq);

Praćenje uspjeha uvoza

Tablicu ImportJob možete koristiti za bilježenje podataka o uspjehu uvoza rješenja. Kada navedete za ImportJobIdImportSolutionRequest, tu vrijednost možete koristiti za upit u tablici ImportJob o statusu uvoza. ImportJobId također se može koristiti za preuzimanje datoteke zapisnika uvoza pomoću poruke Zahtjev za dohvaćanje formatiranih poslova uvoza.

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

Sadržaj entiteta Data jest niz koji predstavlja rješenje XML datoteke.

Dodavanje i uklanjanje komponenti rješenja

Saznajte kako dodati i ukloniti komponente rješenja pomoću koda.

Dodavanje nove komponente rješenja

Ovaj uzorak pokazuje kako stvoriti komponentu rješenja koja je povezana s određenim rješenjem. Ako komponentu rješenja ne povežete s određenim rješenjem kada je stvoreno, ono će se dodati samo zadanom rješenju i morate ga dodati u rješenje ručno ili pomoću koda uključenog u komponentu Dodavanjepostojećeg rješenja.

Ovaj kôd stvara novi globalni skup mogućnosti i dodaje ga rješenju s jedinstvenim nazivom jednakim _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);

Dodavanje postojeće komponente rješenja

Ovaj uzorak pokazuje kako dodati postojeću komponentu rješenja u rješenje.

Sljedeći kôd koristi AddSolutionComponentRequest za dodavanje Account tablice kao komponente rješenja u neupravljano rješenje.

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

Uklanjanje komponente rješenja

Ovaj uzorak pokazuje kako ukloniti postojeću komponentu rješenja iz neupravljanog rješenja. Sljedeći kôd koristi komponentu RemoveSolutionComponentRequest rješenja tablice iz neupravljanog rješenja. solution.UniqueName upućuje na rješenje stvoreno u Stvaranje rješenja bez upravljanja.

// 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 rješenja

Sljedeći uzorak pokazuje kako dohvatiti rješenje korištenjem rješenja uniquename, a zatim izvući solutionid iz rezultata. Uzorak tada koristi solutionid s IOrganizationService. Delete način brisanja rješenja.

// 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, stvaranje zakrpa i nadogradnja

Dodatne operacije rješenja možete izvesti pomoću dostupnih API-ja. Za rješenja za kloniranje i krpanje koristite CloneAsPatchRequest i CloneAsSolutionRequest. Za informacije o kloniranju i primjeni zakrpa pogledajte Stvaranje zakrpa rješenja,

Prilikom nadogradnje rješenja koristite StageAndUpgradeRequest i DeleteAndPromoteRequest. Dodatne informacije o postupku postavljanja i nadogradnje potražite u odjeljku Nadogradnja ili ažuriranje rješenja.

Otkrivanje zavisnosti rješenja

Ovaj uzorak pokazuje kako stvoriti izvješće koje pokazuje ovisnosti između komponenata rješenja.

Ovaj kôd izvodi ove operacije:

  • Dohvatiti sve komponente za rješenje.

  • Dohvatiti sve ovisnosti za svaku komponentu.

  • Za svaku pronađenu ovisnost prikazati izvješće koje opisuje ovisnost.

// 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 u sljedećem uzorku koda.

Izvješće o ovisnosti

Metoda DependencyReport pruža prijateljsku poruku koja se temelji na podacima pronađenima u ovisnosti.

Napomena

U ovom se primjeru metoda samo djelomično provodi. Može prikazivati poruke samo za komponente rješenja atributa i skupa moguć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);
}

Otkrivanje može li se komponenta rješenja izbrisati

Koristite poruku RetrieveDependenciesForDeleteRequest za prepoznavanje svih ostalih komponenti rješenja koje bi spriječile brisanje dane komponente rješenja. Sljedeći primjer koda traži sve atribute pomoću poznatog stupca globalnog izbora. Svaki atribut koji koristi globalni izbor spriječio bi brisanje globalnog izbora.

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