Делите путем


Радите са решењима помоћу Dataverse SDK

Као део вашег развоја до производног животног циклуса, можда ћете желети да креирате прилагођену аутоматизацију за руковање одређеним задацима. На пример, у DevOps каналу пројекта можда ћете желети да извршите неки прилагођени кôд или скрипту која креира sandbox окружење, увози некомплетно решење, извози то некомплетно решење као комплетно и, на крају, брише окружење. Ове операције и још много тога можете обавити помоћу АПИ-ја који су вам доступни. Ево неколико примера онога што можете постићи користећи Dataverse СДК за .НЕТ и прилагођени код.

Белешка

Те исте операције можете изводити и помоћу веб API-ја. Сродне радње су: Увези решење, Извези решење, Клонирај као закрпу и Клонирај као решење.

Узорци кода у овом чланку користе ране типове ентитета генерисане коришћењем ЦрмСвцУтил или ПАЦ ЦЛИ. Више информација: Касно везано и рано везано програмирање помоћу СДК-а за .НЕТ

Креирајте, извезите или увезите некомплетно решење

Научите како да извршите неке уобичајене операције решења помоћу Ц# кода. Да бисте видели комплетан радни узорак Ц # кода који показује ове врсте операција решења и још много тога, идите на Узорак: Рад са решењима.

Креирање издавача

Свако решење захтева издавача, представљен табелом издавача. Издавач захтева следеће особине:

  • Префикс прилагођавања
  • Јединствени назив
  • Препознатљиво име

Белешка

За здрав приступ управљању животним циклусом апликација (АЛМ), увек користите прилагођеног издавача и решење, а не подразумевано решење и издавача, за примену прилагођавања.

Следећи узорак кода прво дефинише издавача, а затим проверава да ли издавач већ постоји на основу јединственог имена. Ако већ постоји, префикс прилагођавања је можда промењен. Овај узорак настоји да ухвати тренутни префикс прилагођавања. PublisherId се такође евидентира па се запис издавача може избрисати. Ако издавач није пронађен, креира се нови издавач помоћу метода IOrganizationService.Креирај.

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

Креирање некомплетног решења

Након што имате доступан прилагођени издавач, можете да креирате некомплетно решење. Следећа табела наводи колоне са описима које решење садржи.

Ознака колоне Опис
Назив за приказ Назив за решење.
Именуј Microsoft Dataverse генерише јединствено име засновано на Имену за приказ. Можете да измените јединствено име. Јединствено име мора да садржи само алфанумеричке знакове или знак подвлаке.
Издавач Користите проналажење Издавача да бисте повезали решење са издавачем.
Верзија Специфицирајте верзију користећи формат: мајор.минор.буилд.ревисион , на пример, #пии_ајхфххгјз.
Страница конфигурисања Ако у своје решење уврстите HTML веб ресурс, можете да користите проналажење да бисте га додали као своју одређену конфигурациону страницу решења.
Опис Користите ову колону да бисте укључили све релевантне детаље о вашем решењу.

Ево примера кода за креирање неуправљаног решења које користи издавача који смо креирали у претходном одељку.

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

Након што креирате неуправљано решење, додајте компоненте решења тако што ћете их креирати у контексту овог решења или додавањем постојећих компоненти из других решења. Још информација: Додавање нове компоненте решења и Додавање постојеће компоненте решења

Извоз некомплетног решења

Овај узорак кода показује како се извози некопмплетно решење или пакет комплетног решења. Кôд користи класу Захтев за извоз решења за извоз компримоване датотеке која представља некомплетно решење. Опција за креирање комплетног решења се подешава помоћу својства Комплетно. Овај пример чува датотеку под називом samplesolution.zip у излазну фасциклу.

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

Увоз некомплетног решења

Увоз (или надоградња) решења коришћењем кода врши се помоћу класе Захтев за увоз решења.

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

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

_serviceProxy.Execute(impSolReq);

Праћење успеха увоза

Можете користити табелу ИмпортЈоб за снимање података о успеху увоза решења. Када наведете ImportJobId за ИмпортСолутионРекуест , можете користити ту вредност за упит табелу ImportJob о статусу увоза. ImportJobId се такође може користити за преузимање датотеке евиденције увоза помоћу поруке Захтев за преузимање форматираних резултата посла увоза.

// 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 је низ који представља XML датотеку решења.

Додавање и уклањање компоненти решења

Научите како да додате и уклоните компоненте решења помоћу кода.

Додавање нове компоненте решења

Овај узорак показује како се креира компонента решења која је повезана са одређеним решењем. Ако не повежете компоненту решења са одређеним решењем када је креирано, она ће бити додата само у подразумевано решење и морате га додати у решење ручно или помоћу кода који је укључен у компоненту Додај постојеће решење.

Овај код ствара нови глобални скуп опција и додаје га у решење са јединственим називом једнаким _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);

Додавање постојеће компоненте решења

Овај узорак показује како додати постојећу компоненту решења у решење.

Следећи код користи AddSolutionComponentRequest да додате Account табелу као компоненту решења у ненадгледано решење.

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

Уклањање компоненте решења

Овај узорак показује како да уклоните компоненту решења из комплетног решења. Следећи код користи RemoveSolutionComponentRequest да уклоните компоненту решења табеле из ненадгледаног решења. solution.UniqueName се референцира на решење креирано у одељку Креирање некомплетног решења.

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

Брисање решења

Следећи узорак показује како се добија решење помоћу решења uniquename, а затим издваја solutionid из резултата. Узорак затим користи solutionid са услугом IOrganizationService. Delete метод за брисање решења.

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

Клонирање, крпљење и надоградња

Можете обавити додатне операције решења користећи расположиве API-је. За клонирање и крпљење решења, користите ЦлонеАсПатцхРеqуест и ЦлонеАсСолутионРекуест. Информације о клонирању и крпљењу потражите у чланку Креирање закрпа за решења.

Приликом надоградње решења користите тастер StageAndUpgradeRequest и DeleteAndPromoteRequest. За више информација о процесу постављања и надоградње, идите на Надоградња или ажурирање решења.

Откривање зависних елемената решења

Овај пример показује како се креира извештај који приказује зависности између компоненти решења.

Овај код обавља ове операције:

  • Вратити све компоненте за решење.

  • Вратити све зависности за сваку компоненту.

  • За сваку нађену зависност приказује се извештај који описује зависност.

// 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 је у следећем узорку кода.

Извештај о зависности

Метода DependencyReport пружа прилагођенију поруку засновану на информацијама које се налазе у зависности.

Белешка

У овом узорку метода се примењује само делимично. Може да приказује поруке само за компоненте атрибута и скуп опција компоненти решења.

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

Откријте да ли компонента решења може бити избрисана

Користите поруку RetrieveDependenciesForDeleteRequest да бисте идентификовали било које друге компоненте решења које спречавају брисање одређене компоненте решења. Следећи узорак кода тражи све атрибуте користећи познату колону глобалног избора. Било који атрибут који користи глобални избор би спречио брисање глобалног избора.

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