Deli putem


Radite sa rešenjima pomoću Dataverse SDK

Kao deo razvoja u životnom ciklusu proizvodnje možda ćete želeti da napravite prilagođenu automatizaciju za rukovanje određenim zadacima. Na primer, u DevOps kanalu projekta možda ćete želeti da izvršite neki prilagođeni kôd ili skriptu koja kreira sandbox okruženje, uvozi nekompletno rešenje, izvozi to nekompletno rešenje kao kompletno i, na kraju, briše okruženje. Možete da uradite ovo i još više koristeći API-je koji su vam dostupni. Sledi nekoliko primera onoga što možete da postignete koristeći Dataverse SDK za .NET i prilagođeni kôd.

Belešku

Te iste operacije možete izvoditi i pomoću Web API-ja. Srodne radnje su: Uvezi rešenje, Izvezi rešenje, Kloniraj kao zakrpu i Kloniraj kao rešenje.

Kreirajte, izvezite ili uvezite nekompletno rešenje

Pogledajmo kako izvoditi neke uobičajene operacije rešenja koristeći C# kôd. Za pregled kompletnog radnog uzorka C# koda koji prikazuje ove vrste operacija rešenja (i više), pogledajte članak Uzorak: rad sa rešenjima.

Kreiranje izdavača

Svako rešenje zahteva izdavača kog predstavlja Entitet izdavača. Izdavač zahteva sledeće:

  • Prefiks prilagođavanja
  • Jedinstveni naziv
  • Prepoznatljivo ime

Belešku

Za zdrav ALM pristup uvek koristite prilagođeni izdavač i rešenje, a ne podrazumevano rešenje i izdavač za primenu prilagođavanja.

Sledeći uzorak koda prvo definiše izdavača, a zatim proverava da li izdavač već postoji na osnovu jedinstvenog imena. Ako već postoji, prefiks prilagođavanja je možda promenjen, tako da ovaj uzorak želi da uhvati trenutni prefiks prilagođavanja. PublisherId se takođe evidentira pa se zapis izdavača može izbrisati. Ako izdavač nije pronađen, kreira se novi izdavač pomoću metoda IOrganizationService.Kreiraj.

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

Kreiranje nekontrolisanog rešenja

Nakon što imate dostupan prilagođeni izdavač, možete da kreirate nekompletno rešenje. Sledeća tabela prikazuje polja sa opisima koje rešenje sadrži.

Oznaka polja Opis
Ime za prikaz Naziv za rešenje.
+Ime Microsoft Dataverse generiše jedinstveno ime zasnovano na Imenu za prikaz. Možete da izmenite jedinstveno ime. Jedinstveno ime mora da sadrži samo alfanumeričke znakove ili znak podvlake.
Izdavač Koristite pronalaženje Izdavača da biste povezali rešenje sa izdavačem.
Verzija Navedite verziju u sledećem formatu: glavna verzija.međuverzija.izdanje.revizija (na primer: 1.0.0.0).
Stranica konfigurisanja Ako u svoje rešenje uvrstite HTML veb-resurs, možete da koristite pronalaženje da biste ga dodali kao svoju određenu konfiguracionu stranicu rešenja.
Opis Koristite ovo polje da biste uključili sve relevantne detalje o vašem rešenju.

Sledi primer uzorka za kreiranje nekompletnog rešenja koje koristi izdavača koji smo kreirali u prethodnom odeljku.

// 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 kreirate nekompletno rešenje, možete da dodate komponente rešenja tako što ćete ih kreirati u kontekstu ovog rešenja ili dodavanjem postojećih komponenti iz drugih rešenja. Još informacija: Dodavanje nove komponente rešenja i Dodavanje postojeće komponente rešenja

Izvoz nekompletnog rešenja

Ovaj uzorak koda pokazuje kako se izvozi nekopmpletno rešenje ili paket kompletnog rešenja. Kôd koristi klasu Zahtev za izvoz rešenja za izvoz komprimovane datoteke koja predstavlja nekompletno rešenje. Opcija za kreiranje kompletnog rešenja se podešava pomoću svojstva Kompletno. Ovaj primer čuva datoteku pod nazivom samplesolution.zip u izlaznu fasciklu.

// 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 nekompletnog rešenja

Uvoz (ili nadogradnja) rešenja korišćenjem koda vrši se pomoću klase Zahtev za uvoz rešenja.

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

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

_serviceProxy.Execute(impSolReq);

Praćenje uspeha uvoza

Entitet posla uvoza možete koristiti za snimanje podataka o uspehu uvoza rešenja. Kada navedete ImportJobId za Zahtev za uvoz rešenja, možete da koristite tu vrednost da biste upitali entitet posla uvoza o statusu uvoza. ImportJobId se takođe može koristiti za preuzimanje datoteke evidencije uvoza pomoću poruke Zahtev za preuzimanje formatiranih rezultata posla 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 svojstva Data je niz koji predstavlja XML datoteku rešenja.

Dodavanje i uklanjanje komponenti rešenja

Naučite kako da dodate i uklonite komponente rešenja pomoću koda.

Dodavanje nove komponente rešenja

Ovaj uzorak pokazuje kako se kreira komponenta rešenja koja je povezana sa određenim rešenjem. Ako komponentu rešenja ne povežete sa određenim rešenjem kada se kreira, dodaće se samo podrazumevanom rešenju i moraćete da je dodate u rešenje ručno ili korišćenjem koda koji je uključen u Dodajte postojeću komponentu rešenja.

Ovaj kod stvara novi globalni skup opcija i dodaje ga u rešenje sa 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 rešenja

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

Sledeći kôd koristi AddSolutionComponentRequest za dodavanje entiteta Account kao komponente rešenja za nekompletno reš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 rešenja

Ovaj uzorak pokazuje kako da uklonite komponentu rešenja iz kompletnog rešenja. Sledeći kôd koristi RemoveSolutionComponentRequest za uklanjanje entiteta komponente rešenja iz nekompletnog rešenja. solution.UniqueName se referencira na rešenje kreirano u odeljku Kreiranje nekompletnog rešenja.

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

Sledeći uzorak pokazuje kako se dobija rešenje pomoću rešenja uniquename, a zatim izdvaja solutionid iz rezultata. Uzorak zatim koristi solutionid sa uslugom IOrganizationService. Delete metod za brisanje reš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, krpljenje i nadogradnja

Možete obaviti dodatne operacije rešenja koristeći raspoložive API-je. Za kloniranje i krpljenje rešenja koristite CloneAsPatchRequest i CloneAsSolutionRequest. Informacije o kloniranju i krpljenju potražite u članku Kreiranje zakrpa za rešenja.

Prilikom nadogradnje rešenja koristite taster StageAndUpgradeRequest i DeleteAndPromoteRequest. Za više informacija o procesu faze i nadogradnje pogledajte Nadogradnja ili ažuriranje rešenja.

Otkrivanje zavisnih elemenata rešenja

Ovaj primer pokazuje kako se kreira izveštaj koji prikazuje zavisnosti između komponenti rešenja.

Ovaj kôd će:

  • Vratiti sve komponente za rešenje.

  • Vratiti sve zavisnosti za svaku komponentu.

  • Za svaku nađenu zavisnost prikazuje se izveštaj koji opisuje zavisnost.

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

Izveštaj o zavisnosti

Metoda DependencyReport pruža prilagođeniju poruku zasnovanu na informacijama koje se nalaze u zavisnosti.

Belešku

U ovom uzorku metoda se primenjuje samo delimično. Može da prikazuje poruke samo za komponente atributa i skup opcija komponenti rešenja.

/// <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 da li se komponenta rešenja može izbrisati

Koristite poruku RetrieveDependenciesForDeleteRequest da biste identifikovali bilo koje druge komponente rešenja koje sprečavaju brisanje određene komponente rešenja. Sledeći uzorak koda traži sve atribute koristeći poznati globalni skup opcija. Bilo koji atribut koji koristi globalni skup opcija sprečio bi brisanje globalnog skupa opcija.

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