Kongsi melalui


Usahakan penyelesaian menggunakan SDK Dataverse

Sebagai sebahagian daripada pembangunan anda ke kitaran hayat pengeluaran, anda mungkin mahu mencipta automasi tersuai untuk mengendalikan tugas tertentu. Sebagai contoh, dalam talian paip projek DevOps anda mungkin mahu melaksanakan beberapa kod atau skrip tersuai yang mencipta persekitaran kotak pasir, mengimport penyelesaian tidak terurus, mengeksport penyelesaian tidak terurus sebagai penyelesaian terurus dan akhirnya memadam persekitaran. Anda boleh melakukan ini dan banyak lagi dengan menggunakan API yang tersedia untuk anda. Di bawah adalah beberapa contoh perkara yang boleh anda capai dengan menggunakan SDK Dataverse untuk .NET dan kod tersuai.

Nota

Anda juga boleh melaksanakan operasi yang sama menggunakan API Web. Tindakan yang berkaitan adalah: ImportSolution, ExportSolution, CloneAsPatch dan CloneAsSolution.

Cipta, eksport atau import penyelesaian tidak terurus

Mari lihat cara melaksanakan beberapa operasi penyelesaian biasa dengan menggunakan kod C#. Untuk melihat sampel kod C# yang lengkap yang menunjukkan jenis operasi penyelesaian ini (dan banyak lagi), lihat Sampel: Bekerja dengan penyelesaian.

Cipta penerbit

Setiap penyelesaian memerlukan penerbit yang diwakili oleh Entiti penerbit. Penerbit memerlukan yang berikut:

  • Awalan penyesuaian
  • Nama unik
  • Nama mesra

Nota

Untuk pendekatan ALM yang sihat, selalu gunakan penerbit dan penyelesaian tersuai, bukan penyelesaian dan penerbit lalai untuk melaksanakan penyesuaian anda.

Sampel kod berikut terlebih dahulu mentakrifkan penerbit dan kemudian menyemak untuk melihat sama ada penerbit sudah wujud berdasarkan pada nama unik. Jika sudah ada, awalan penyesuaian mungkin telah diubah, oleh itu sampel ini bertujuan untuk merekodkan awalan penyesuaian semasa. PublisherId juga direkodkan supaya rekod penerbit dapat dipadam. Jika penerbit tidak ditemui, penerbit baharu akan dicipta menggunakan IOrganizationService. Kaedah Cipta.

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

Cipta penyelesaian tidak terurus

Setelah anda mempunyai penerbit tersuai yang tersedia, anda kemudian boleh mencipta penyelesaian tidak terurus. Jadual berikut menyenaraikan medan dengan perihalan yang mengandungi penyelesaian.

Label Medan Perihalan
Nama Paparan Nama untuk penyelesaian.
Nama Microsoft Dataverse menjana nama unik berdasarkan Nama Paparan. Anda boleh mengedit nama unik. Nama unik hanya boleh mengandungi aksara abjad angka atau aksara garis bawah.
Penerbit Gunakan carian Penerbit untuk mengaitkan penyelesaian dengan penerbit.
Versi Tentukan versi dengan menggunakan format berikut: major.minor.build.revision (contohnya, 1.0.0.0.
Halaman Konfigurasi Jika anda sertakan sumber Web HTML dalam penyelesaian anda, anda boleh menggunakan carian ini untuk menambahkannya sebagai halaman konfigurasi penyelesaian yang anda ditentukan.
Penerangan Gunakan medan ini untuk memasukkan butiran yang berkaitan tentang penyelesaian anda.

Di bawah adalah kod sampel untuk mencipta penyelesaian tidak terurus yang menggunakan penerbit yang kami cipta di bahagian sebelumnya.

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

Setelah anda mencipta penyelesaian tidak terurus, anda boleh menambahkan komponen penyelesaian dengan menciptanya dalam konteks penyelesaian ini atau dengan menambahkan komponen sedia ada daripada penyelesaian lain. Maklumat penyelesaian: Tambahkan komponen penyelesaian baharu dan Tambahkan komponen penyelesaian sedia ada

Eksport penyelesaian tidak terurus

Sampel kod ini menunjukkan cara mengeksport penyelesaian tidak terurus atau pakej penyelesaian terurus. Kod menggunakan kelas ExportSolutionRequest untuk mengeksport fail termampat mewakili penyelesaian tak terurus. Pilihan untuk mencipta penyelesaian terurus ditetapkan dengan menggunakan sifat Terurus. Sampel ini menyimpan fail bernama samplesolution.zip kepada folder output.

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

Import penyelesaian tidak terurus

Mengimport (atau menaik taraf) penyelesaian dengan menggunakan kod dicapai dengan ImportSolutionRequest.

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

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

_serviceProxy.Execute(impSolReq);

Menjejaki kejayaan import

Anda boleh menggunakan entiti ImportJob untuk merekodkan data tentang kejayaan import penyelesaian. Apabila anda menentukan ImportJobId untuk ImportSolutionRequest, anda boleh menggunakan nilai tersebut untuk pertanyaan entiti ImportJob tentang status import. ImportJobId juga boleh digunakan untuk memuat turun fail log import menggunakan mesej 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);
   }
}

Kandungan sifat Data adalah rentetan yang mewakili fail XML penyelesaian.

Tambah dan alih keluar komponen penyelesaian

Ketahui cara menambah dan mengalih keluar komponen penyelesaian menggunakan kod.

Tambah komponen penyelesaian baharu

Sampel ini menunjukkan cara mencipta komponen penyelesaian yang berkaitan dengan penyelesaian tertentu. Jika anda tidak mengaitkan komponen penyelesaian kepada penyelesaian tertentu apabila ia dicipta ia hanya akan ditambah kepada penyelesaian lalai dan anda perlu menambahnya kepada penyelesaian secara manual atau dengan menggunakan kod yang disertakan dalam Tambah komponen penyelesaian sedia ada.

Kod ini mencipta set pilihan global baharu dan menambahnya kepada penyelesaian dengan nama unik yang sama dengan _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);

Tambah komponen penyelesaian sedia ada

Sampel ini menunjukkan cara menambah komponen penyelesaian sedia ada kepada penyelesaian.

Kod berikut menggunakan AddSolutionComponentRequest untuk menambah entiti Account sebagai komponen penyelesaian kepada penyelesaian tidak terurus.

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

Alih keluar komponen penyelesaian

Sampel ini menunjukkan cara alih keluar komponen penyelesaian daripada penyelesaian tidak terurus. Kod berikut menggunakan RemoveSolutionComponentRequest untuk alih keluar komponen penyelesaian entiti daripada penyelesaian tidak terurus. Rujukan penyelesaian solution.UniqueName yang dicipta dalam Cipta penyelesaian tidak terurus.

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

Padam penyelesaian

Sampel berikut menunjukkan cara untuk mendapatkan kembali penyelesaian menggunakan penyelesaian uniquename dan kemudian mengekstrak solutionid daripada keputusan. Sampel kemudian menggunakan solutionid denganIOrganizationService. Kaedah Delete untuk memadam penyelesaian.

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

Pengklonan, penampalan dan menaik taraf

Anda boleh melakukan operasi penyelesaian tambahan menggunakan APIs yang tersedia. Untuk penyelesaian pengklonan dan penampalan menggunakan CloneAsPatchRequest dan CloneAsSolutionRequest. Untuk maklumat tentang pengklonan dan penampalan, lihat Cipta tampalan penyelesaian.

Apabila melakukan naik taraf penyelesaian menggunakan StageAndUpgradeRequest dan DeleteAndPromoteReques. Untuk maklumat lanjut mengenai proses pementasan dan naik taraf, lihat Naik Taraf atau kemas kini penyelesaian.

Kesan kebergantungan penyelesaian

Sampel ini menunjukkan cara mencipta laporan yang menunjukkan kebergantungan antara komponen penyelesaian.

Kod ini akan:

  • Dapatkan semula semua komponen untuk penyelesaian.

  • Dapatkan kembali semua kebergantungan untuk setiap komponen.

  • Bagi setiap kebergantungan yang ditemui memaparkan laporan yang menerangkan kebergantungan.

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

Kaedah DependencyReport ada dalam sampel kod berikut.

Laporan kebergantungan

Kaedah DependencyReport memberikan mesej yang lebih mesra berdasarkan maklumat yang ditemui dalam kebergantungan.

Nota

Dalam sampel ini kaedah hanya dilaksanakan sebahagiannya. Ia boleh memaparkan mesej hanya untuk atribut dan komponen penyelesaian set pilihan.

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

Kesan sama ada komponen penyelesaian yang mungkin dipadamkan

Gunakan mesej RetrieveDependenciesForDeleteRequest untuk mengenal pasti sebarang komponen penyelesaian lain yang akan mencegah komponen penyelesaian yang diberikan daripada dipadamkan. Sampel kod berikut mencari sebarang atribut menggunakan optionset global yang diketahui. Sebarang atribut menggunakan optionset global akan mencegah optionset global daripada dipadamkan.

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