Megoldások használata Dataverse SDK-val
A fejlesztéstől az éles használatig tartó életciklus részeként célszerű lehet egyéni automatizálást létrehozni bizonyos feladatok kezelésére. Például a DevOps projektfolyamatban célszerű lehet néhány egyéni programkódot vagy parancsfájlt végrehajtani, amely tesztkörnyezetet hoz létre, importál egy nem felügyelt megoldást, exportálja ezt a nem felügyelt megoldást felügyelt megoldásként, végül pedig törli a környezetet. Mindezt és még többet megtehet az Ön számára elérhető alkalmazásprogramozási felületek használatával. Az alábbiakban néhány példát talál arra, hogy mit tud elérni a Dataverse SDK a .NET és az egyéni kódok használatával.
Megjegyzés
Ugyanezeket a műveleteket a webes API segítségével is végrehajthatja. A kapcsolódó műveletek a következők: ImportSolution, ExportSolution, CloneAsPatch és CloneAsSolution.
Nézzük meg, hogyan hajthat végre néhány gyakori megoldást a C#-kód használatával. Ha meg szeretné tekinteni a teljes működő C#-kód mintáját, amely bemutatja az ilyen típusú megoldások működését (és egyebeket), tekintse meg a következőt: Minta: Megoldások használata.
Minden megoldáshoz egy közzétevő szükséges, amelyet a Közzétevő entitás képvisel. A közzétevőnek a következőkre van szüksége:
- Testreszabási előtag
- Egyedi név
- Felhasználóbarát név
Megjegyzés
A testreszabások telepítéséhez az egészséges ALM-megközelítés érdekében mindig egyéni közzétevőt és megoldást használjon, ne az alapértelmezett megoldást és közzétevőt.
A következő kódminta először meghatároz egy közzétevőt, majd ellenőrzi, hogy a közzétevő már létezik-e az egyedi név alapján. Ha már létezik, előfordulhat, hogy a testreszabási előtag módosult, így a példa az aktuális testreszabási előtag rögzítésére törekszik. A PublisherId
is rögzítésre kerül, hogy a közzétevői rekord törölhető legyen. Ha a közzétevő nem található, egy új közzétevő jön létre az IOrganizationService.Create módszer használatával.
// 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;
}
Ha van elérhető egyéni közzétevő, akkor ezt követően létrehozhatja a nem felügyelt megoldást. A következő táblázat felsorolja azokat a mezőket leírással együtt, amelyeket a megoldás tartalmaz.
Mező címkéje | Leírás |
---|---|
Megjelenítendő név | A megoldás neve. |
Név szerint | A Microsoft Dataverse egyedi nevet hoz létre a Megjelenítendő név alapján. Az egyedi név szerkeszthető. Az egyedi név csak alfanumerikus karaktereket és aláhúzást tartalmazhat. |
Kiadó | A Közzétevő keresése segítségével társíthatja a megoldást egy közzétevőhöz. |
Verzió | A verziószámot a következő formátum használatával adja meg: főverzió.alverzió.build.revízió, például: 1.0.0.0. |
Konfigurációs oldal | Ha a megoldásba HTML-alapú weberőforrást foglal bele, akkor a keresés segítségével hozzáadhatja azt a kijelölt mogoldás konfigurációs oldalhoz. |
Ismertetés | Ebben a mezőben adhat meg a megoldásra vonatkozó információkat. |
Az alábbi mintakód egy nem felügyelt megoldás létrehozására szolgál, amely az előző szakaszban létrehozott közzétevőt használja.
// 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);
}
A nem felügyelt megoldás létrehozása után hozzáadhat megoldás-összetevőket, létrehozva őket a megoldás kontextusában, vagy meglévő összetevőket hozzáadva más megoldásokból. További információ: Új megoldás-összetevő hozzáadása és Meglévő megoldás-összetevő hozzáadása
Ez a mintakód azt mutatja be, hogyan exportálható a nem felügyelt megoldás vagy csomagolható egy felügyelt megoldás. A kód az ExportSolutionRequest osztály segítségével exportálja a nem felügyelt megoldást képviselő tömörített fájlt. A felügyelt megoldás létrehozására szolgáló beállítás a Managed tulajdonság használatával állítható be. Ez a példa egy samplesolution.zip nevű fájlt ment a kimeneti mappába.
// 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);
A megoldások kód használatával történő importálása (vagy frissítése) az ImportSolutionRequest segítségével végezhető el.
// Install or upgrade a solution
byte[] fileBytes = File.ReadAllBytes(ManagedSolutionLocation);
ImportSolutionRequest impSolReq = new ImportSolutionRequest()
{
CustomizationFile = fileBytes
};
_serviceProxy.Execute(impSolReq);
A ImportJob entitás segítségével rögzítheti a megoldás importálásának sikerével kapcsolatos adatokat. Ha megad egy ImportJobId
elemet az ImportSolutionRequest számára, akkor az adott érték segítségével lekérdezheti az ImportJob entitást az importálás állapotáról. Az ImportJobId
használható az importálási naplófájl letöltésére is a RetrieveFormattedImportJobResultsRequest üzenet használatával.
// 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);
}
}
A Data
tulajdonság tartalma a megoldás XML-fájlját jelképező karakterlánc.
Megismerheti, hogyan adhat hozzá és távolíthat el megoldás-összetevőket programkód használatával.
Ez a példa azt mutatja be, hogyan lehet létrehozni egy adott megoldáshoz társított megoldás-összetevőt. Ha a létrehozáskor nem társítja a megoldás-összetevőt egy konkrét megoldáshoz, akkor csak az alapértelmezett megoldáshoz lesz hozzáadva, és manuálisan vagy a Meglévő megoldás-összetevő hozzáadása arészben szereplő kóddal kell felvennie a megoldásba.
Ez a kód új globális értékkészlet hoz létre, és azt a megoldáshoz adja a _primarySolutionName
értékkel megegyező egyedi névvel.
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);
Ez a példa azt mutatja be, hogyan lehet egy meglévő megoldás-összetevőt hozzáadni egy megoldáshoz.
A következő kód az AddSolutionComponentRequest használatával adja hozzá az Account
entitást megoldás-összetevőként egy nem felügyelt megoldáshoz.
// 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);
Ez a példa azt mutatja be, hogyan lehet egy meglévő megoldás-összetevőt eltávolítani egy nem felügyelt megoldásból. A következő kód az RemoveSolutionComponentRequest használatával távolítja el az entitás megoldás-összetevőt egy nem felügyelt megoldásból. A solution.UniqueName
a Nem felügyelt megoldás létrehozása részben létrehozott megoldásra hivatkozik.
// 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);
A következő példa azt mutatja be, hogyan lehet a uniquename
megoldás segítségével lekérni a megoldást, majd az eredményből kibontani a solutionid
elemet. A minta ezt követően a solutionid
elemet használja a IOrganizationService elemmel. Delete módszer a megoldás törléséhez.
// 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);
A rendelkezésre álló API-k segítségével további megoldásműveleteket hajthat végre. Megoldások klónozásához és javításához használja a következőket: CloneAsPatchRequest és CloneAsSolutionRequest. A klónozással és a javítással kapcsolatban lásd: Megoldásjavítások létrehozása.
A megoldások frissítéseinek végrehajtásakor használja a StageAndUpgradeRequest és a DeleteAndPromoteRequest elemet. Az állomásoztatás és frissítések folyamatáról a Megoldás frissítése és verziófrissítése című rész tartalmaz további tudnivalókat.
Ez a példa azt mutatja be, hogyan lehet létrehozni egy jelentést, amely bemutatja a megoldás-összetevők közötti függőségeket.
A kód a következőkre szolgál:
A megoldáshoz tartozó összes összetevő lekérése.
Az egyes összetevőkhöz tartozó összes függőség lekérése.
Minden megtalált függőségnél a függőséget leíró jelentés jeleníti meg.
// 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);
}
}
A DependencyReport
módszer a következő kódmintában található.
A DependencyReport
módszer a függőségben található információk alapján felhasználóbarátabb üzenetet biztosít.
Megjegyzés
Ebben a példában a módszer csak részben kerül megvalósításra. Csak attribútum és értékkészlet megoldás-összetevőkhöz jeleníti meg az üzeneteket.
/// <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);
}
Az RetrieveDependenciesForDeleteRequest üzenet segítségével azonosíthat minden egyéb megoldás-összetevőt, amely megakadályozná egy adott megoldás-összetevő törlését. A következő kódminta egy ismert globális értékkészlet segítségével keresi meg az összes attribútumot. A globális értékkészletet használó attribútumok bármelyike megakadályozza a globális értékkészket törlését.
// 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);
}
}
}