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