Dataverse SDK kullanarak çözümlerle çalışma

Üretim yaşam döngüsü geliştirmelerinizin parçası olarak, belirli görevleri işlemek için özel otomasyon oluşturmak isteyebilirsiniz. Örneğin, DevOps proje ardışık düzeninde, sandbox ortamı oluşturan, yönetilmeyen bir çözümü alan ve bu yönetilmeyen çözümü yönetilen çözüm olarak veren bazı özel kod veya komut dosyalarını çalıştırmak isteyebilirsiniz ve son olarak ortamı siler. Kullanılabilir API 'Leri kullanarak bunu ve daha fazlasını yapabilirsiniz. Aşağıda, .NET için Dataverse SDK ve özel kod kullanarak yapabileceklerinize örnekler verilmektedir.

Not

Ayrıca, Web API'yi kullanarak da aynı işlemleri gerçekleştirebilirsiniz. İlgili eylemler şunlardır: ImportSolution, ExportSolution, CloneAsPatch ve CloneAsSolution.

Yönetilmeyen bir çözümü oluşturma, dışarı veya içeri aktarma

C# kodunu kullanarak bazı yaygın çözüm işlemlerini nasıl gerçekleştirebileceğinizi görelim. Bu tür çözüm işlemlerini (ve daha fazlasını) gösteren tüm çalışma C# kod örneğini görüntülemek için bkz. Örnek: çözümlerle çalışma.

Bir Yayımcı oluşturma

Her çözüm, Yayımcı varlığı tarafından temsil edilen bir yayımcı gerektirir. Yayımcı aşağıdakileri ister:

  • Özelleştirme öneki
  • Benzersiz Ad
  • Kolay ad

Not

İyi durumda bir ALM yaklaşımı için, özelleştirmelerinizi dağıtmak için varsayılan çözüm ve yayımcı değil, her zaman özel bir yayımcı ve çözüm kullanın.

Aşağıdaki kod örneğinde ilk olarak bir yayımcı tanımlanır ve benzersiz ada göre yayıncının zaten varolup olmadığı denetlenir. Zaten varsa, özelleştirme öneki değişmiş olabilir, bu nedenle bu örnek geçerli özelleştirme önekini yakalamaya göre arar. Ayrıca PublisherId, yayımcı kaydının silinebilmesi için kaydedilir. Yayımcı bulunamazsa IOrganizationService.Create yöntemi kullanılarak yeni bir yayımcı oluşturulur.

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

Yönetilmeyen çözüm oluşturma

Kullanılabilir özel bir yayıncısını aldıktan sonra, bir yönetilmeyen çözüm oluşturabilirsiniz. Aşağıdaki tabloda, bir çözümün içerdiği açıklamalarla açıklamalar bulunan alanlar listelenmektedir.

Alan Etiketi Açıklama
Görünen Ad Çözüm adı.
Ad Microsoft Dataverse, Görünen Ad'a dayalı benzersiz bir ad oluşturur. Benzersiz adı düzenleyebilirsiniz. Bnezersiz ad yalnızca alfasayısal karakterleri ve alt çizgi karakterini içerebilir.
Yayımlayan Çözümü bir yayımcı ile ilişkilendirmek için Yayımcı aramasını kullanın.
Sürüm Lütfen şu biçimde bir sürüm belirtin major.minor.build.revision, (örneğin, 1.0.0.0.
Yapılandırma Sayfası Çözümünüze bir HTML Web kaynağı eklerseniz, bu aramayı kullanarak onu belirlenen çözüm yapılandırma sayfası olarak ekleyebilirsiniz.
Açıklama Çözümünüz hakkında ilgili ayrıntıları dahil etmek için bu alanı kullanın.

Önceki bölümde oluşturduğum yayımcıyı kullanan yönetilmeyen bir çözüm oluşturmak için aşağıdaki örnek kod yer örneğidir.

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

Bir yönetilmeyen çözümü oluşturduktan sonra, bu çözüm bağlamında oluşturarak ya da diğer çözümlerdeki varolan bileşenleri ekleyerek çözüm bileşenleri ekleyebilirsiniz. Daha fazla bilgi: Yeni bir çözüm bileşeni ekleme ve varolan bir çözüm bileşenini ekleme

Yönetilmeyen çözümü dışa aktarma

Bu kod örneği, yönetilmeyen bir çözümün nasıl verileceğini veya yönetilen çözüm bir paket oluşturulacağını gösterir. Kod, yönetilmeyen çözümü temsil eden sıkıştırılmış bir dosyayı dışarı aktarmak için ExportSolutionRequest sınıfını kullanır. Yönetilen çözüm oluşturma seçeneği, Yönetilen özellik kullanılarak ayarlanır. Bu örnek, samplesolution.zip adlı bir dosyayı çıktı klasörüne kaydeder.

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

Yönetilmeyen çözüm alma

Kod kullanarak bir çözümün alınması (veya yükseltilmesi) ImportSolutionRequest ile gerçekleştirilir.

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

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

_serviceProxy.Execute(impSolReq);

Alma başarısını izleme

Alma işi varlığını, çözüm almanın başarısıyla ilgili verileri yakalamak için kullanabilirsiniz. Importsolutionrequest için bir ImportJobId belirttiğinizde Bu değeri, almanın durumu hakkında ImportJob varlığını sorgulamak için kullanabilirsiniz. ImportJobId, RetrieveFormattedImportJobResultsRequest iletisi kullanılarak bir alma günlüğü dosyasını karşıdan yüklemek için de kullanılabilir .

// 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 özelliğin içeriği, çözüm XML dosyasını gösteren bir dizedir.

Çözüm bileşenlerini ekleme ve kaldırma

Kod kullanarak çözüm bileşenlerinin eklemeyi ve kaldırmayı öğrenin.

Yeni çözüm bileşeni ekleme

Bu örnekte, belirli bir çözümle ilişkili çözüm bileşeninin nasıl oluşturulacağı gösterilmektedir. Çözüm bileşenini oluşturulduğu sırada belirli bir çözümle ilişkilendirmezseniz, yalnızca varsayılan çözüme eklenir ve el ile veya Varolan bir çözüm bileşenini ekleme bileşeninde bulunan kodu kullanarak bir çözüme eklemeniz gerekir.

Bu kod, yeni bir global seçenek kümesi oluşturur ve _primarySolutionName değerine eş benzersiz adla çözüme ekler.

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

Mevcut bir çözüm bileşeni ekleme

Bu örnek, varolan bir çözüm bileşeninin bir çözüme nasıl ekleneceğini göstermektedir.

Aşağıdaki kod, Account varlığını bir çözüm bileşeni olarak yönetilmeyen çözüme eklemek için AddSolutionComponentRequest kullanır.

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

Bir çözüm bileşenini kaldırma

Bu örnek, bir çözüm bileşeninin yönetilmeyen bir çözümden nasıl kaldırılacağını göstermektedir. Aşağıdaki kod, bir varlığı yönetilmeyen bir çözümden kaldırmak için RemoveSolutionComponentRequest kullanır. solution.UniqueName, Yönetilmeyen çözüm oluşturma adımında oluşturulan çözüme başvuruda bulunur.

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

Çözüm silme

Aşağıdaki örnek, uniquename çözümünü kullanarak bir çözümün nasıl alınacağını ve sonra da sonuçlardan solutionid öğesinin nasıl ayıklanacağını gösterir. Örnek daha sonra IOrganizationService ile solutionid öğesini kullanır. Çözümü silmek için Delete yöntemi.

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

Klonlama, düzeltme eki ve yükseltme

Kullanılabilir API'leri kullanarak ek çözüm işlemleri gerçekleştirebilirsiniz. Çözümleri klonlama ve yama uygulama için CloneAsPatchRequest ve CloneAsSolutionRequest öğesini kullanın. Kopyalama ve düzeltme ile ilgili bilgi için bkz. Çözüm düzeltme ekleri oluşturma.

Çözüm yükseltmeleri gerçekleştirirken StageAndUpgradeRequest ve DeleteAndPromoteRequest öğesini kullanın. Hazırlama ve yükseltme işlemi hakkında Daha fazla bilgi edinmek için bkz. Çözüm yükseltme veya güncelleştirme.

Çözüm bağımlılıklarını algılama

Bu örnekte, çözüm bileşenleri arasındaki bağımlılıkları gösteren bir raporun nasıl oluşturulacağı gösterilmektedir.

Bu kod:

  • Bir çözüm için tüm bileşenleri alır.

  • Her bileşen için tüm bağımlılıkları alır.

  • Bulunan her bağımlılık için, bağımlılığı tanımlayan bir rapor görüntüler.

// 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 yöntemi aşağıdaki kod örneğinde yer almaktadır.

Bağımlılık raporu

DependencyReport yöntemi, bağımlılık içinde bulunan bilgilere dayalı olarak, daha kolay bir ileti sağlar.

Not

Bu örnekte, yöntem yalnızca kısmen uygulanmıştır. Yalnızca öznitelik ve seçenek kümesi çözüm bileşenleri için ileti görüntüleyebilir.

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

Bir çözüm bileşeninin silinip silinemeyeceğini algılama

Belirli bir çözüm bileşeninin silinmesini önleyen tüm diğer çözüm bileşenlerini belirlemek için RetrieveDependenciesForDeleteRequest iletisini kullanın. Aşağıdaki kod örneği, bilinen bir global seçenek kümesi kullanan tüm öznitelikleri arar. Global seçenek kümesini kullanan tüm öznitelikler global seçenek kümesinin silinmesini önler.

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