Notă
Accesul la această pagină necesită autorizare. Puteți încerca să vă conectați sau să modificați directoarele.
Accesul la această pagină necesită autorizare. Puteți încerca să modificați directoarele.
Ca parte a ciclului de viață de la dezvoltare la producție, este posibil să doriți să creați automatizări personalizate pentru a gestiona anumite sarcini. De exemplu, în canalul de proiect DevOps, s-ar putea să doriți să executați un cod sau script particularizat care creează un mediu de tip sandbox, importă o soluție negestionată, exportă soluția care negestionată ca soluție gestionată și, în cele din urmă, șterge mediul. Puteți face aceste operațiuni și multe altele utilizând API-urile disponibile. Iată câteva exemple despre ceea ce puteți realiza folosind Dataverse SDK-ul pentru .NET și cod personalizat.
Notă
Puteți efectua, de asemenea, aceleași operațiuni utilizând API Web. Acțiunile conexe sunt: ImportSolution, ExportSolution, CloneAsPatch și CloneAsSolution.
Exemplele de cod din acest articol utilizează tipuri de entități cu legături timpurii, generate fie cu CrmSvcUtil, fie cu PAC CLI. Mai multe informații: Programare cu limitări târzii și timpurii folosind SDK-ul pentru .NET
Creați, exportați sau importați o soluție negestionată
Aflați cum să efectuați câteva operațiuni comune de rezolvare folosind cod C#. Pentru a vizualiza exemplul complet de cod C# funcțional care demonstrează aceste tipuri de operațiuni de soluționare și multe altele, accesați Exemplu: Lucrul cu soluții.
Creați un editor
Fiecare soluție necesită un editor, reprezentat de tabelul Publisher. Un editor necesită următoarele proprietăți:
- Un prefix particularizare
- Un nume unic
- Un nume prietenos
Notă
Pentru o abordare sănătoasă a gestionării ciclului de viață al aplicațiilor (ALM), utilizați întotdeauna un editor și o soluție personalizate, nu soluția și editorul implicite, pentru implementarea particularizărilor.
Următorul exemplu de cod definește mai întâi un editor, apoi verifică dacă editorul există deja, pe baza numelui unic. Dacă există deja, este posibil ca prefixul de personalizare să fi fost modificat. Acest exemplu încearcă să surprindă prefixul de personalizare curent.
PublisherId este de asemenea capturat astfel încât înregistrarea editorului să poată fi ștearsă. Dacă editorul nu este găsit, un editor nou este creat folosind metoda IOrganizationService.Creare .
// 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;
}
Crearea unei soluții negestionate
După ce aveți un editor particularizat disponibil, puteți crea apoi o soluție negestionată. Următorul tabel listează coloanele cu descrieri pe care le conține o soluție.
| Etichetă coloană | Descriere |
|---|---|
| Nume afișat | Numele soluției. |
| Nume | Microsoft Dataverse generează un nume unic bazat pe Numele afișat. Aveți posibilitatea să editați numele unic.. Numele unic trebuie să conțină numai caractere alfanumerice sau caracterul de subliniere. |
| Editor | Folosește căutarea Editor pentru a asocia soluția cu un editor. |
| Versiune | Specificați o versiune utilizând formatul: major.minor.build.revision, de exemplu, 1.0.0.0. |
| Pagină de configurare | Dacă includeți o resursă Web HTML în soluția dvs., puteți utiliza această căutare pentru a o adăuga ca pagina de configurare soluție desemnată. |
| Descriere | Folosește această coloană pentru a include orice detalii relevante despre soluția ta. |
Iată un exemplu de cod pentru a crea o soluție negestionată care utilizează editorul pe care l-am creat în secțiunea anterioară.
// 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);
}
După ce creați o soluție negestionată, adăugați componente ale soluției creându-le în contextul acestei soluții sau adăugând componente existente din alte soluții. Mai multe informații: Adăugați o componentă nouă de soluțieși Adăugați o componentă de soluție existentă
Exportați o soluție negestionată
Acest eșantion de cod arată cum se poate exporta o soluție negestionată sau realiza ca pachet o soluție gestionată. Codul folosește clasa ExportSolutionRequest pentru a exporta un fișier comprimat reprezentând o soluție negestionată. Opțiunea de a crea o soluție gestionată este setată folosind proprietatea Gestionat. Acest exemplu salvează un fișier numit samplesolution.zip la folderul de ieșire.
// 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);
Importul unei soluții negestionate
Importul (sau upgradarea) unei soluții prin utilizarea codului se realizează cu Import Solicitare Soluție.
// Install or upgrade a solution
byte[] fileBytes = File.ReadAllBytes(ManagedSolutionLocation);
ImportSolutionRequest impSolReq = new ImportSolutionRequest()
{
CustomizationFile = fileBytes
};
_serviceProxy.Execute(impSolReq);
Urmărirea succesului la import
Puteți utiliza tabelul ImportJob pentru a captura date despre succesul importului soluției. Când specificați o ImportJobId valoare pentru ImportSolutionRequest, puteți utiliza acea valoare pentru a interoga tabelul despre starea importului. ImportJob
ImportJobId poate fi, de asemenea, utilizat pentru a descărca un fișier jurnal de import folosind mesajul 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);
}
}
Conținutul proprietății Data este un șir care reprezintă fișierul XML al soluției.
Adăugarea sau eliminarea componentelor soluției
Aflați cum puteți adăuga și elimina componentele soluției folosind codul.
Adăugarea unei noi componente de soluție
Acest eșantion arată cum puteți crea o componentă de soluție care este asociată cu o soluție specifică. Dacă nu asociați componenta soluției cu o anumită soluție la crearea acesteia, aceasta va fi adăugată doar la soluția implicită și trebuie să o adăugați manual la o soluție sau utilizând codul inclus în secțiunea Adăugați o componentă de soluție existentă.
Acest cod creează un nou set de opțiuni global și îl adaugă la soluție cu un nume unic egal cu _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);
Adăugarea unei componente de soluție existente
Acest eșantion arată cum se adaugă o componentă de soluție existentă la o soluție.
Următorul cod folosește AddSolutionComponentRequest pentru a adăuga Account tabelul ca și componentă a soluției la o soluție negestionată.
// 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);
Eliminarea unei componente a soluției
Acest eșantion arată cum se elimină o componentă de soluție dintr-o soluție negestionată. Următorul cod folosește RemoveSolutionComponentRequest pentru a elimina o componentă de soluție tabelară dintr-o soluție negestionată.
solution.UniqueName face referire la soluția creată în Creați o soluție negestionată.
// 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);
Ştergerea unei soluții
Următorul eșantion arată cum să regăsiți o soluție folosind soluția uniquename apoi să extrageți solutionid din rezultate. Eșantionul utilizează apoi solutionid cuIOrganizationService.
Delete metodă de ștergere a soluției.
// 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);
Clonare, corectare și upgrade
Puteți efectua operațiuni suplimentare de soluție utilizând API-urile disponibile. Pentru soluții de clonare și aplicare a patch-urilor, utilizați CloneAsPatchRequest și CloneAsSolutionRequest. Pentru informații despre clonare și corectare, consultați Creați corectură soluție.
Când efectuați actualizări ale soluției, utilizați StageAndUpgradeRequest și DeleteAndPromoteRequest. Pentru mai multe informații despre procesul de implementare și actualizări, accesați Actualizarea unei soluții.
Detectarea dependențelor soluțiilor
Acest exemplu arată cum se creează un raport care arată dependențele dintre componentele soluției.
Acest cod efectuează următoarele operațiuni:
Recupera toate componentele pentru o soluție.
Recupera toate dependențele pentru fiecare componentă.
Pentru fiecare dependență găsită se afișează un raport care descrie dependența.
// 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 se află în următorul eșantion de cod.
Raport dependență
Metoda DependencyReport oferă un mesaj mai prietenos bazat pe informațiile găsite în dependență.
Notă
În acest eșantion, metoda este implementată doar parțial. Poate afișa mesaje doar pentru componentele de soluție ale atributului și ale setului de opțiuni.
/// <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);
}
Detectează dacă o componentă a soluției poate fi ștearsă
Folosiți mesajul RetrieveDependenciesForDeleteRequest pentru a identifica orice alte componente ale soluției care ar împiedica ștergerea unei componente a soluției date. Următorul exemplu de cod caută orice atribute utilizând o coloană de alegere globală cunoscută. Orice atribut care utilizează alegerea globală ar împiedica ștergerea opțiunii globale.
// 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);
}
}
}