Skip to content

Commit dc11d67

Browse files
Add migration guide (Azure#19456)
* Add migration guide * Fix deserialization snippet * relative link * Apply suggestions from code review Co-authored-by: Jesse Squire <jesse.squire@gmail.com> * PR FB * explicit type Co-authored-by: Jesse Squire <jesse.squire@gmail.com>
1 parent 236016b commit dc11d67

File tree

4 files changed

+216
-9
lines changed

4 files changed

+216
-9
lines changed

sdk/eventgrid/Azure.Messaging.EventGrid/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,13 +213,14 @@ Once events are delivered to the event handler, parse the JSON payload into list
213213
Using `EventGridEvent`:
214214
```C# Snippet:EGEventParseJson
215215
// Parse the JSON payload into a list of events
216-
EventGridEvent[] egEvents = EventGridEvent.ParseMany(jsonPayloadSampleOne);
216+
EventGridEvent[] egEvents = EventGridEvent.ParseMany(BinaryData.FromStream(httpContent));
217217
```
218218

219219
Using `CloudEvent`:
220220
```C# Snippet:CloudEventParseJson
221+
var bytes = await httpContent.ReadAsByteArrayAsync();
221222
// Parse the JSON payload into a list of events
222-
CloudEvent[] cloudEvents = CloudEvent.ParseMany(jsonPayloadSampleTwo);
223+
CloudEvent[] cloudEvents = CloudEvent.ParseMany(new BinaryData(bytes));
223224
```
224225
From here, one can access the event data by deserializing to a specific type using `GetData<T>()`. Calling `GetData()` will either return the event data wrapped in `BinaryData`, which represents the serialized JSON event data as bytes.
225226

sdk/eventgrid/Azure.Messaging.EventGrid/samples/Sample3_ParseAndDeserializeEvents.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ Once events are delivered to the event handler, parse the JSON payload into list
1414
Using `EventGridEvent`:
1515
```C# Snippet:EGEventParseJson
1616
// Parse the JSON payload into a list of events
17-
EventGridEvent[] egEvents = EventGridEvent.ParseMany(jsonPayloadSampleOne);
17+
EventGridEvent[] egEvents = EventGridEvent.ParseMany(BinaryData.FromStream(httpContent));
1818
```
1919

2020
Using `CloudEvent`:
2121
```C# Snippet:CloudEventParseJson
22+
var bytes = await httpContent.ReadAsByteArrayAsync();
2223
// Parse the JSON payload into a list of events
23-
CloudEvent[] cloudEvents = CloudEvent.ParseMany(jsonPayloadSampleTwo);
24+
CloudEvent[] cloudEvents = CloudEvent.ParseMany(new BinaryData(bytes));
2425
```
2526

2627
## Deserialize Event Data
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# Guide for migrating to Azure.Messaging.EventGrid from Microsoft.Azure.EventGrid
2+
3+
This guide is intended to assist in the migration to `Azure.Messaging.EventGrid` from `Microsoft.Azure.EventGrid`. It will focus on side-by-side comparisons for similar operations between the two packages.
4+
5+
We assume that you are familiar with `Microsoft.Azure.EventGrid`. If not, please refer to the [README](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/eventgrid/Azure.Messaging.EventGrid/README.md) for `Azure.Messaging.EventGrid` rather than this guide.
6+
7+
## Table of contents
8+
- [Migration benefits](#migration-benefits)
9+
- [Cross Service SDK improvements](#cross-service-sdk-improvements)
10+
- [New features](#new-features)
11+
- [Important changes](#important-changes)
12+
- [Package names and namespaces](#package-names-and-namespaces)
13+
- [Client naming and constructors](#client-naming-and-constructors)
14+
- [Publishing events to a topic](#publishing-events-to-a-topic)
15+
- [Deserializing events and data](#deserializing-events-and-data)
16+
- [Additional samples](#additional-samples)
17+
18+
## Migration benefits
19+
20+
As Azure has matured and been embraced by a more diverse group of developers, we have been focused on learning the patterns and practices to best support developer productivity and to understand the gaps that the .NET client libraries have.
21+
22+
There were several areas of consistent feedback expressed across the Azure client library ecosystem. One of the most important is that the client libraries for different Azure services have not had a consistent approach to organization, naming, and API structure. Additionally, many developers have felt that the learning curve was difficult, and the APIs did not offer a good, approachable, and consistent onboarding story for those learning Azure or exploring a specific Azure service.
23+
24+
To improve the development experience across Azure services, a set of uniform [design guidelines](https://azure.github.io/azure-sdk/general_introduction.html) was created for all languages to drive a consistent experience with established API patterns for all services. A set of [.NET design guidelines](https://azure.github.io/azure-sdk/dotnet_introduction.html) was also introduced to ensure that .NET clients have a natural and idiomatic feel with respect to the .NET ecosystem. The new Azure.Messaging.EventGrid library follows these guidelines.
25+
26+
### Cross Service SDK improvements
27+
28+
The modern `Azure.Messaging.EventGrid` client library also provides the ability to share in some of the cross-service improvements made to the Azure development experience, such as:
29+
30+
- Using the new Azure.Identity library to share a single authentication approach between clients
31+
- A unified logging and diagnostics pipeline offering a common view of the activities across each of the client libraries
32+
33+
### New features
34+
35+
The new Azure.Messaging.EventGrid library includes several new features:
36+
- Publishing and deserializing [CloudEvents](https://cloudevents.io/) using the Azure.Core `CloudEvent` type
37+
- Methods that allow publishing a single event in addition to lists of events
38+
- Ability to use a custom serializer for the Data of an event
39+
- Ability to publish events with a custom schema
40+
- Generate a shared access signature token using the `EventGridSasBuilder`
41+
42+
## Important changes
43+
44+
### Package names and namespaces
45+
46+
Package names and the namespace root for the modern Azure client libraries for .NET have changed. Each will follow the pattern `Azure.[Area].[Service]` where the legacy clients followed the pattern `Microsoft.Azure.[Service]`. This provides a quick and accessible means to help understand, at a glance, whether you are using the modern or legacy clients.
47+
48+
In the case of Event Grid, the modern client libraries have packages and namespaces that begin with Azure.Messaging.EventGrid and were released beginning with version 4. The legacy client libraries have packages and namespaces that begin with Microsoft.Azure.EventGrid and a version of 3.x.x or below.
49+
50+
### Client naming and constructors
51+
52+
In `Microsoft.Azure.EventGrid`, the `EventGridClient` was instantiated by passing the `TopicCredentials` into the constructor:
53+
```C#
54+
string topicEndpoint = "https://<topic-name>.<region>-1.eventgrid.azure.net/api/events";
55+
string topicKey = "<topic-key>";
56+
57+
TopicCredentials topicCredentials = new TopicCredentials(topicKey);
58+
EventGridClient client = new EventGridClient(topicCredentials);
59+
```
60+
61+
In the `Azure.Messaging.EventGrid` library, the client used to interact with the service was renamed as the `EventGridPublisherClient` to highlight the fact that the client is only used to publish events and does not consume events from the service. The constructor takes the topic endpoint in addition to a credential parameter since a single client will only ever be able to publish to a single topic.
62+
```C# Snippet:CreateClientWithOptions
63+
EventGridPublisherClient client = new EventGridPublisherClient(
64+
new Uri(topicEndpoint),
65+
new AzureKeyCredential(topicAccessKey));
66+
```
67+
68+
### Publishing events to a topic
69+
70+
In the `Microsoft.Azure.EventGrid` library, events were published as shown:
71+
```C#
72+
string topicEndpoint = "https://<topic-name>.<region>-1.eventgrid.azure.net/api/events";
73+
string topicHostname = new Uri(topicEndpoint).Host;
74+
75+
List<EventGridEvent> eventsList = new List<EventGridEvent>();
76+
for (int i = 0; i < 1; i++)
77+
{
78+
eventsList.Add(new EventGridEvent()
79+
{
80+
Id = Guid.NewGuid().ToString(),
81+
EventType = "Contoso.Items.ItemReceivedEvent",
82+
Data = new ContosoItemReceivedEventData()
83+
{
84+
ItemUri = "ContosoSuperItemUri"
85+
},
86+
87+
EventTime = DateTime.Now,
88+
Subject = "Door1",
89+
DataVersion = "2.0"
90+
});
91+
}
92+
93+
await client.PublishEventsAsync(topicHostname, eventsList);
94+
```
95+
96+
In `Azure.Messaging.EventGrid`, when publishing events it is no longer necessary to include the topic host name. Instead, this is passed as part of the `Uri` that is provided to the `EventGridPublisherClient` constructor. The `EventGridEvent` type now has a constructor that includes parameters for all required fields of the event grid schema:
97+
```C# Snippet:SendEGEventsToTopic
98+
// Add EventGridEvents to a list to publish to the topic
99+
List<EventGridEvent> eventsList = new List<EventGridEvent>
100+
{
101+
new EventGridEvent(
102+
"ExampleEventSubject",
103+
"Example.EventType",
104+
"1.0",
105+
"This is the data for the first event"),
106+
new EventGridEvent(
107+
"ExampleEventSubject",
108+
"Example.EventType",
109+
"1.0",
110+
"This is the data for the second event")
111+
};
112+
113+
// Send the events
114+
await client.SendEventsAsync(eventsList);
115+
```
116+
### Deserializing events and data
117+
118+
In `Microsoft.Azure.EventGrid`, there was the `EventGridSubscriber` type that was used to deserialize events:
119+
```C#
120+
string requestContent = await req.Content.ReadAsStringAsync();
121+
122+
EventGridSubscriber eventGridSubscriber = new EventGridSubscriber();
123+
124+
// Optionally add one or more custom event type mappings
125+
eventGridSubscriber.AddOrUpdateCustomEventMapping("Contoso.Items.ItemReceived", typeof(ContosoItemReceivedEventData));
126+
127+
EventGridEvent[] events = eventGridSubscriber.DeserializeEventGridEvents(requestContent);
128+
129+
foreach (EventGridEvent receivedEvent in events)
130+
{
131+
if (receivedEvent.Data is SubscriptionValidationEventData)
132+
{
133+
SubscriptionValidationEventData eventData = (SubscriptionValidationEventData)receivedEvent.Data;
134+
log.Info($"Got SubscriptionValidation event data, validationCode: {eventData.ValidationCode}, validationUrl: {eventData.ValidationUrl}, topic: {eventGridEvent.Topic}");
135+
// Handle subscription validation
136+
}
137+
else if (receivedEvent.Data is StorageBlobCreatedEventData)
138+
{
139+
StorageBlobCreatedEventData eventData = (StorageBlobCreatedEventData)receivedEvent.Data;
140+
log.Info($"Got BlobCreated event data, blob URI {eventData.Url}");
141+
// Handle StorageBlobCreatedEventData
142+
}
143+
else if (receivedEvent.Data is ContosoItemReceivedEventData)
144+
{
145+
ContosoItemReceivedEventData eventData = (ContosoItemReceivedEventData)receivedEvent.Data;
146+
}
147+
}
148+
```
149+
150+
In the `Azure.Messaging.EventGrid` library, there is no longer a separate type to deserialize events. Instead, you can use static factory methods on `EventGridEvent` to parse into an array of `EventGridEvents`.
151+
```C# Snippet:EGEventParseJson
152+
// Parse the JSON payload into a list of events
153+
EventGridEvent[] egEvents = EventGridEvent.ParseMany(BinaryData.FromStream(httpContent));
154+
```
155+
156+
You can then iterate through your events and deserialize the data.
157+
```C# Snippet:DeserializePayloadUsingAsSystemEventData
158+
foreach (EventGridEvent egEvent in egEvents)
159+
{
160+
// If the event is a system event, TryGetSystemEventData will return the deserialized system event
161+
if (egEvent.TryGetSystemEventData(out object systemEvent))
162+
{
163+
switch (systemEvent)
164+
{
165+
case SubscriptionValidationEventData subscriptionValidated:
166+
Console.WriteLine(subscriptionValidated.ValidationCode);
167+
break;
168+
case StorageBlobCreatedEventData blobCreated:
169+
Console.WriteLine(blobCreated.BlobType);
170+
break;
171+
// Handle any other system event type
172+
default:
173+
Console.WriteLine(egEvent.EventType);
174+
// we can get the raw Json for the event using Data
175+
Console.WriteLine(egEvent.Data.ToString());
176+
break;
177+
}
178+
}
179+
else
180+
{
181+
switch (egEvent.EventType)
182+
{
183+
case "MyApp.Models.CustomEventType":
184+
TestPayload deserializedEventData = egEvent.Data.ToObjectFromJson<TestPayload>();
185+
Console.WriteLine(deserializedEventData.Name);
186+
break;
187+
// Handle any other custom event type
188+
default:
189+
Console.Write(egEvent.EventType);
190+
Console.WriteLine(egEvent.Data.ToString());
191+
break;
192+
}
193+
}
194+
}
195+
```
196+
197+
## Additional samples
198+
199+
More examples can be found at:
200+
- [Event Grid samples](https://github.com/Azure/azure-sdk-for-net/tree/master/sdk/eventgrid/Azure.Messaging.EventGrid/samples)

sdk/eventgrid/Azure.Messaging.EventGrid/tests/Samples/Sample2_ParseAndDeserializeEvents.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
// Licensed under the MIT License.
33

44
using System;
5+
using System.Net.Http;
56
using System.Text.Json;
7+
using System.Threading.Tasks;
68
using Azure.Core.Serialization;
79
using Azure.Core.TestFramework;
810
using Azure.Messaging.EventGrid.SystemEvents;
@@ -13,17 +15,18 @@ namespace Azure.Messaging.EventGrid.Tests.Samples
1315
public partial class EventGridSamples : SamplesBase<EventGridTestEnvironment>
1416
{
1517
// Example JSON payloads
16-
private readonly BinaryData jsonPayloadSampleOne = new BinaryData("[{ \"id\": \"2d1781af-3a4c\", \"topic\": \"/examples/test/payload\", \"subject\": \"\", \"data\": { \"Name\": \"example\",\"Age\": 20 },\"eventType\": \"MyApp.Models.CustomEventType\",\"eventTime\": \"2018-01-25T22:12:19.4556811Z\",\"dataVersion\": \"1\"}]");
18+
private readonly string jsonPayloadSampleOne = "[{ \"id\": \"2d1781af-3a4c\", \"topic\": \"/examples/test/payload\", \"subject\": \"\", \"data\": { \"Name\": \"example\",\"Age\": 20 },\"eventType\": \"MyApp.Models.CustomEventType\",\"eventTime\": \"2018-01-25T22:12:19.4556811Z\",\"dataVersion\": \"1\"}]";
1719

18-
private readonly BinaryData jsonPayloadSampleTwo = new BinaryData("[{ \"id\": \"2d1781af-3a4c\", \"source\": \"/examples/test/payload\", \"data\": { \"name\": \"example\",\"age\": 20 },\"type\": \"MyApp.Models.CustomEventType\",\"time\": \"2018-01-25T22:12:19.4556811Z\",\"specversion\": \"1.0\"}]");
20+
private readonly string jsonPayloadSampleTwo = "[{ \"id\": \"2d1781af-3a4c\", \"source\": \"/examples/test/payload\", \"data\": { \"name\": \"example\",\"age\": 20 },\"type\": \"MyApp.Models.CustomEventType\",\"time\": \"2018-01-25T22:12:19.4556811Z\",\"specversion\": \"1.0\"}]";
1921

2022
// This sample demonstrates how to parse EventGridEvents from JSON and access event data using TryGetSystemEventData
2123
[Test]
2224
public void NonGenericReceiveAndDeserializeEventGridEvents()
2325
{
26+
var httpContent = new BinaryData(jsonPayloadSampleOne).ToStream();
2427
#region Snippet:EGEventParseJson
2528
// Parse the JSON payload into a list of events
26-
EventGridEvent[] egEvents = EventGridEvent.ParseMany(jsonPayloadSampleOne);
29+
EventGridEvent[] egEvents = EventGridEvent.ParseMany(BinaryData.FromStream(httpContent));
2730
#endregion
2831

2932
// Iterate over each event to access event properties and data
@@ -70,7 +73,7 @@ public void NonGenericReceiveAndDeserializeEventGridEvents()
7073

7174
// This sample demonstrates how to parse CloudEvents from JSON and access event data using ToObjectFromJson
7275
[Test]
73-
public void GenericReceiveAndDeserializeEventGridEvents()
76+
public async Task GenericReceiveAndDeserializeEventGridEvents()
7477
{
7578
// Example of a custom ObjectSerializer used to deserialize the event payload
7679
JsonObjectSerializer myCustomSerializer = new JsonObjectSerializer(
@@ -79,9 +82,11 @@ public void GenericReceiveAndDeserializeEventGridEvents()
7982
PropertyNameCaseInsensitive = true
8083
});
8184

85+
var httpContent = new StreamContent(new BinaryData(jsonPayloadSampleTwo).ToStream());
8286
#region Snippet:CloudEventParseJson
87+
var bytes = await httpContent.ReadAsByteArrayAsync();
8388
// Parse the JSON payload into a list of events
84-
CloudEvent[] cloudEvents = CloudEvent.ParseMany(jsonPayloadSampleTwo);
89+
CloudEvent[] cloudEvents = CloudEvent.ParseMany(new BinaryData(bytes));
8590
#endregion
8691

8792
// Iterate over each event to access event properties and data

0 commit comments

Comments
 (0)