Skip to content

Commit

Permalink
Merge pull request #643 from microsoft/netcore-conversions
Browse files Browse the repository at this point in the history
Code sample conversions to .NET Core
  • Loading branch information
phecke authored Dec 10, 2024
2 parents a0f1cba + 98a2698 commit d9c72e0
Show file tree
Hide file tree
Showing 22 changed files with 10,360 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>MyApp.DataModel</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" />
<PackageReference Include="Microsoft.PowerPlatform.Dataverse.Client" Version="1.2.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\DataModel\DataModel.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="letter.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.35327.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActivityParty", "ActivityParty.csproj", "{B9737429-4E81-445C-B64B-EBD7C85FB53E}"
ProjectSection(ProjectDependencies) = postProject
{3EA8AF9E-7639-4C91-B7A8-313BCD3826D2} = {3EA8AF9E-7639-4C91-B7A8-313BCD3826D2}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataModel", "..\..\DataModel\DataModel.csproj", "{3EA8AF9E-7639-4C91-B7A8-313BCD3826D2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B9737429-4E81-445C-B64B-EBD7C85FB53E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B9737429-4E81-445C-B64B-EBD7C85FB53E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B9737429-4E81-445C-B64B-EBD7C85FB53E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B9737429-4E81-445C-B64B-EBD7C85FB53E}.Release|Any CPU.Build.0 = Release|Any CPU
{3EA8AF9E-7639-4C91-B7A8-313BCD3826D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3EA8AF9E-7639-4C91-B7A8-313BCD3826D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3EA8AF9E-7639-4C91-B7A8-313BCD3826D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3EA8AF9E-7639-4C91-B7A8-313BCD3826D2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {875F57F0-1C51-4061-88EA-22A2192B62EC}
EndGlobalSection
EndGlobal
259 changes: 259 additions & 0 deletions dataverse/orgsvc/C#-NETCore/Activities/ActivityParty/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
using Microsoft.Extensions.Configuration;
using Microsoft.PowerPlatform.Dataverse.Client;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Messages;
using MyApp.DataModel;

namespace PowerPlatform_Dataverse_CodeSamples
{
internal class Program
{
/// <summary>
/// Contains the application's configuration settings.
/// </summary>
IConfiguration Configuration { get; }

/// <summary>
/// Constructor. Loads the application settings from a JSON configuration file.
/// </summary>
Program()
{
// Get the path to the appsettings file. If the environment variable is set,
// use that file path. Otherwise, use the runtime folder's settings file.
string? path = Environment.GetEnvironmentVariable("DATAVERSE_APPSETTINGS");
if (path == null) path = "appsettings.json";

// Load the app's configuration settings from the JSON file.
Configuration = new ConfigurationBuilder()
.AddJsonFile(path, optional: false, reloadOnChange: true)
.Build();
}

static void Main(string[] args)
{
// Entity name and reference collection.
Dictionary<string,EntityReference> entityStore;

Program app = new();

// Create a Dataverse service client using the default connection string.
ServiceClient serviceClient =
new(app.Configuration.GetConnectionString("default"));

// Pre-create any table rows that CreateLetter() requires.
app.Setup(serviceClient, out entityStore);

// Execute the main logic of this program
app.CreateLetter(serviceClient, entityStore);

// Pause program execution before resource cleanup.
Console.WriteLine("Press any key to undo environment data changes.");
Console.ReadKey();

// In Dataverse, delete any created table rows and then dispose the service connection.
app.Cleanup(serviceClient, entityStore);
serviceClient.Dispose();
}

/// <summary>
/// Create a letter activity from a contact and addressed to two other contacts.
/// </summary>
/// <param name="service">Authenticated web service connection.</param>
/// <param name="entityStore">Collection containing references for three contacts
/// to be referred to by the letter.</param>
/// <returns>True if successful; otherwise false.</returns>
public bool CreateLetter(IOrganizationService service,
Dictionary<string, EntityReference> entityStore)
{
// Use the OrganizationServiceContext class and create a LINQ query.
var orgContext = new OrganizationServiceContext(service);

// Create an Activity Party (in-memory) object for each contact.
var activityParty1 = new ActivityParty
{
PartyId = new EntityReference(
Contact.EntityLogicalName, entityStore["contact1"].Id)
};

var activityParty2 = new ActivityParty
{
PartyId = new EntityReference(
Contact.EntityLogicalName, entityStore["contact2"].Id)
};

var activityParty3 = new ActivityParty
{
PartyId = new EntityReference(
Contact.EntityLogicalName, entityStore["contact3"].Id)
};

// Create a Letter activity and set From and To fields to the
// respective Activity Party rows.

var letter = new Letter
{
RegardingObjectId = new EntityReference(Contact.EntityLogicalName,
entityStore["contact2"].Id),
Subject = "Sample Letter Activity",
ScheduledEnd = DateTime.Now + TimeSpan.FromDays(5),
Description = File.ReadAllText("letter.txt"),
From = new ActivityParty[] { activityParty1 },
To = new ActivityParty[] { activityParty3, activityParty2 }
};

// Add the letter activity to the context.
orgContext.AddObject(letter);

try
{
// Commit the context changes to Dataverse.
SaveChangesResultCollection results =
orgContext.SaveChanges(SaveChangesOptions.None);

// Check for success and handle failure.
if (results.Count > 0 && results[0].Error == null)
{
CreateResponse response = (CreateResponse)results[0].Response;

entityStore.Add(letter.Subject,
new EntityReference("letter", response.id ));
return true;
}
else
{
Console.WriteLine(
"CreateLetter(): an error ocurred creating the letter activity: \n\t" +
results[0].Error.Message);
return false;
}
}
catch (Exception ex)
{
Console.WriteLine(
"CreateLetter(): an exception ocurred creating the Letter Activity: \n\t"+ex.Message);
return false;
}
}

/// <summary>
/// Create three contacts.
/// </summary>
/// <param name="service">Authenticated web service connection.</param>
/// <param name="entityStore">Entity name and reference collection.</param>
public void Setup(IOrganizationService service, out Dictionary<string,
EntityReference> entityStore)
{
var contact1 = new Entity("contact")
{
["firstname"] = "Mary Kay",
["lastname"] = "Andersen",
["address1_line1"] = "23 Market St.",
["address1_city"] = "Sammamish",
["address1_stateorprovince"] = "MT",
["address1_postalcode"] = "99999",
["telephone1"] = "12345678",
["emailaddress1"] = "[email protected]"
};

var contact2 = new Entity("contact")
{
["firstname"] = "Joe",
["lastname"] = "Andreshak",
["address1_line1"] = "23 Market St.",
["address1_city"] = "Sammamish",
["address1_stateorprovince"] = "MT",
["address1_postalcode"] = "99999",
["telephone1"] = "12345678",
["emailaddress1"] = "[email protected]"
};

var contact3 = new Entity("contact")
{
["firstname"] = "Denise",
["lastname"] = "Smith",
["address1_line1"] = "23 Market St.",
["address1_city"] = "Sammamish",
["address1_stateorprovince"] = "MT",
["address1_postalcode"] = "99999",
["telephone1"] = "12345678",
["emailaddress1"] = "[email protected]"
};

entityStore = new Dictionary<string, EntityReference>();

try
{
contact1.Id = service.Create(contact1);
entityStore.Add("contact1", new EntityReference("contact",contact1.Id));

contact2.Id = service.Create(contact2);
entityStore.Add("contact2", new EntityReference("contact", contact2.Id));

contact3.Id = service.Create(contact3);
entityStore.Add("contact3", new EntityReference("contact", contact3.Id));
}
catch (Exception ex)
{
Console.WriteLine($"Setup(): an error ocurred creating table row data. \n\t"+ex.Message);
Console.WriteLine($"Setup(): some contacts could not be created.");
throw;
}

}

/// <summary>
/// Dispose of any data and resources created by the this program.
/// </summary>
/// <param name="service">Authenticated web service connection.</param>
/// <param name="entityStore">Entity name and reference collection.</param>
public void Cleanup(ServiceClient service,
Dictionary<string, EntityReference> entityStore)
{
// Do some checking of the passed parameter values.
if (service == null || service.IsReady == false)
{
Console.WriteLine("Cleanup(): web service connection is not available, cleanup aborted.");
return;
}

if (entityStore == null)
{
Console.WriteLine("Cleanup(): entref store collection is null, cleanup aborted.");
Console.WriteLine("Cleanup(): be sure to run Setup() prior to Cleanup().");
return;
}

// Collect the keys of entities to be deleted.
var keysToDelete = new List<string>(entityStore.Keys);

// Delete in Dataverse each entity in the entity store.
foreach (var key in keysToDelete)
{
var entref = entityStore[key];
try
{
service.Delete(entref.LogicalName, entref.Id);
entityStore.Remove(key);
}
catch (Exception ex)
{
Console.WriteLine($"Cleanup(): exception deleting {key}\n\t{ex.Message}");
continue;
}
}

// Output a list of entities that could not be deleted.
if (entityStore.Count > 0)
{
Console.WriteLine("Cleanup(): the following entities (tablle rows) could not be deleted:");
foreach (var item in entityStore)
{
Console.WriteLine($"Cleanup(): name={item.Key}, " +
$"logical name={item.Value.LogicalName}, ID={item.Value.Id}");
}
Console.WriteLine("Cleanup(): consider deleting these rows manually.");
}
}
}
}
53 changes: 53 additions & 0 deletions dataverse/orgsvc/C#-NETCore/Activities/ActivityParty/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
languages:
- csharp
products:
- power-platform
- power-apps
page_type: sample
description: "This sample demonstrates how to create a letter activity."
---

# Create a letter activity

Learn how to create a letter activity addressed to multiple contacts.

Related article: [Activity tables](https://learn.microsoft.com/power-apps/developer/data-platform/activity-entities)

## About the sample code

|Sample|Description|Build target|
|---|---|---|
|ActivityParty|Demonstrates creating a letter activity.|.NET 9|

The code sample demonstrates how to create a letter activity. Specifically, the samples demonstrates how to:

1. Connect to Dataverse using a [connection string](https://learn.microsoft.com/power-apps/developer/data-platform/xrm-tooling/use-connection-strings-xrm-tooling-connect) that defines required connection information
1. Create a [letter activity](https://learn.microsoft.com/power-apps/developer/data-platform/reference/entities/letter) to send to multiple [contacts](https://learn.microsoft.com/power-apps/developer/data-platform/reference/entities/contact)
1. Use the Dataverse [organization service context](https://learn.microsoft.com/power-apps/developer/data-platform/org-service/organizationservicecontext) to process the data changes
1. Use [early-bound](https://learn.microsoft.com/power-apps/developer/data-platform/org-service/early-bound-programming#early-bound) entity types

The code being demonstrated can be found in the `Program.Run()` method.

The early-bound entity files in the *DataModel* project were generated using the following PAC CLI command:
`pac modelbuilder build`. More information: [pac modelbuilder](https://learn.microsoft.com/en-us/power-platform/developer/cli/reference/modelbuilder)

Additional general information can be found in [README-code-design](https://github.com/microsoft/PowerApps-Samples/tree/master/dataverse/orgsvc/C%23-NETCore/README-code-design.md) file.

## How to build and run the code sample(s)

1. Clone the [PowerApps-Samples](https://github.com/microsoft/PowerApps-Samples) repository.
1. Locate the sample folder.
1. Open the solution file (*.sln) in Visual Studio.
1. Edit the project's appsettings.json file and set the `Url`value as appropriate for your Dataverse test environment.
1. Build and run the project [F5].
1. You will be prompted in a browser window for account logon credentials to the target environment.

## Expected program output

For a successful run, the program's console output should look similar to the following example.
Otherwise, any errors or exceptions will be displayed.

```console
Press any key to undo environment data changes.
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"ConnectionStrings": {
"default": "AuthType=OAuth;Url=https://myorg.crm.dynamics.com;RedirectUri=http://localhost;AppId=51f81489-12ee-4a9e-aaae-a2591f45987d;LoginPrompt=Auto"
},
"Logging": {
"LogLevel": {
"Default": "Trace"
}
}
}
Loading

0 comments on commit d9c72e0

Please sign in to comment.