microsoft/teams.net
Publicmirrored fromhttps://github.com/microsoft/teams.netAvailable
core/docs/MigrationGuide.md
387lines · modecode
| 1 | # Migration Guide: Libraries/Microsoft.Teams.Apps to core/src/Microsoft.Teams.Apps |
| 2 | |
| 3 | This guide covers migrating from the old `Microsoft.Teams.Apps` library (`Libraries/`) to the new `Microsoft.Teams.Apps` library (`core/src/`). |
| 4 | |
| 5 | --- |
| 6 | |
| 7 | ## Assembly Mapping |
| 8 | |
| 9 | The old library is split across 16 assemblies. The new library consolidates into 3. |
| 10 | |
| 11 | ### New library assemblies |
| 12 | |
| 13 | | New Assembly | Purpose | |
| 14 | |---|---| |
| 15 | | `Microsoft.Teams.Core` | Foundation: activity protocol, auth, middleware, HTTP clients | |
| 16 | | `Microsoft.Teams.Apps` | High-level: handlers, routing, OAuth flows, API clients | |
| 17 | | `Microsoft.Teams.Apps.BotBuilder` | Backward compat layer for Bot Framework SDK | |
| 18 | |
| 19 | ### Old assemblies not available in the new library |
| 20 | |
| 21 | These assemblies have no equivalent in the new library and must be sourced separately or replaced: |
| 22 | |
| 23 | | Old Assembly | Status | |
| 24 | |---|---| |
| 25 | | `Microsoft.Teams.AI` | Not available | |
| 26 | | `Microsoft.Teams.AI.Models.OpenAI` | Not available | |
| 27 | | `Microsoft.Teams.Cards` | Not available | |
| 28 | | `Microsoft.Teams.Extensions.Graph` | Not available | |
| 29 | | `Microsoft.Teams.Plugins.AspNetCore.DevTools` | Not available | |
| 30 | | `Microsoft.Teams.Plugins.External.Mcp` | Not available — plugin architecture removed | |
| 31 | | `Microsoft.Teams.Plugins.External.McpClient` | Not available — plugin architecture removed | |
| 32 | | `Microsoft.Teams.Apps.Testing` | Not available — use standard DI mocking instead of `TestPlugin` | |
| 33 | |
| 34 | ### Old assemblies replaced by standard .NET |
| 35 | |
| 36 | | Old Assembly | Replaced By | |
| 37 | |---|---| |
| 38 | | `Microsoft.Teams.Common` (logging) | `Microsoft.Extensions.Logging` | |
| 39 | | `Microsoft.Teams.Common` (HTTP) | `System.Net.Http.HttpClient` + DI | |
| 40 | | `Microsoft.Teams.Common` (storage) | No direct replacement — `IStorage<K,V>` removed | |
| 41 | | `Microsoft.Teams.Extensions.Configuration` | `Microsoft.Extensions.Configuration` via `BotConfig` | |
| 42 | | `Microsoft.Teams.Extensions.Logging` | `Microsoft.Extensions.Logging` (no bridge needed) | |
| 43 | | `Microsoft.Teams.Extensions.Hosting` | `TeamsBotApplicationHostingExtensions` | |
| 44 | | `Microsoft.Teams.Plugins.AspNetCore` | Standard ASP.NET Core middleware + `BotApplication.ProcessAsync()` | |
| 45 | | `Microsoft.Teams.Plugins.AspNetCore.BotBuilder` | `Microsoft.Teams.Apps.BotBuilder` (compat layer) | |
| 46 | |
| 47 | --- |
| 48 | |
| 49 | ## Quick Reference |
| 50 | |
| 51 | | Old API | New API | Notes | |
| 52 | |---------|---------|-------| |
| 53 | | `builder.AddTeams()` | `builder.AddTeams()` | Now works on both `WebApplicationBuilder` and `IServiceCollection` | |
| 54 | | `context.Send("text", ct)` | `context.Send("text", ct)` | Same API | |
| 55 | | `context.Send(activity, ct)` | `context.Send(activity, ct)` | Same API | |
| 56 | | `context.Reply("text", ct)` | `context.Reply("text", ct)` | Same API | |
| 57 | | `context.Reply(activity, ct)` | `context.Reply(activity, ct)` | Same API | |
| 58 | | `context.Typing("text", ct)` | `context.Typing("text", ct)` | Same API | |
| 59 | | `context.Log.Info(...)` | `context.Log.Info(...)` | Same API, delegates to `ILogger` | |
| 60 | | `context.Log.Error(...)` | `context.Log.Error(...)` | Same API | |
| 61 | | `context.Log.Debug(...)` | `context.Log.Debug(...)` | Same API | |
| 62 | | `context.Log.Warn(...)` | `context.Log.Warn(...)` | Same API | |
| 63 | | `context.AppId` | `context.AppId` | Same API | |
| 64 | | `teams.OnMeetingJoin(h)` | `teams.OnMeetingJoin(h)` | Alias for `OnMeetingParticipantJoin` | |
| 65 | | `teams.OnMeetingLeave(h)` | `teams.OnMeetingLeave(h)` | Alias for `OnMeetingParticipantLeave` | |
| 66 | | `teams.Send(convId, text)` | `teams.Send(convId, text)` | Proactive messaging | |
| 67 | | `teams.Reply(convId, msgId, text)` | `teams.Reply(convId, msgId, text)` | Proactive threaded reply | |
| 68 | | `InvokeResponse(200, body)` | `InvokeResponse.Ok(body)` | Factory method available | |
| 69 | | `InvokeResponse(400, body)` | `InvokeResponse.Error(400, body)` | Factory method available | |
| 70 | |
| 71 | --- |
| 72 | |
| 73 | ## Backward-Compatible Changes (No Migration Needed) |
| 74 | |
| 75 | These APIs have been added to the new library to match the old API surface. Existing code using these patterns will work without changes. |
| 76 | |
| 77 | ### Context Convenience Methods (BC-1) |
| 78 | |
| 79 | The following methods are available on `Context<TActivity>`: |
| 80 | |
| 81 | ```csharp |
| 82 | // Send a text message |
| 83 | await context.Send("Hello!", cancellationToken); |
| 84 | |
| 85 | // Send an activity |
| 86 | await context.Send(myActivity, cancellationToken); |
| 87 | |
| 88 | // Send a threaded reply |
| 89 | await context.Reply("This is a reply", cancellationToken); |
| 90 | await context.Reply(myActivity, cancellationToken); |
| 91 | |
| 92 | // Send typing indicator |
| 93 | await context.Typing(cancellationToken: cancellationToken); |
| 94 | ``` |
| 95 | |
| 96 | > **Note:** `Send(AdaptiveCard)` and `Reply(AdaptiveCard)` are not yet available to avoid a dependency on `Microsoft.Teams.Cards`. Use `TeamsActivityBuilder` with `AddAdaptiveCardAttachment()` instead. |
| 97 | |
| 98 | ### Context Logger (BC-2) |
| 99 | |
| 100 | `context.Log` provides `.Info()`, `.Error()`, `.Debug()`, and `.Warn()` methods: |
| 101 | |
| 102 | ```csharp |
| 103 | context.Log.Info("Processing message"); |
| 104 | context.Log.Error("Something failed", ex.Message); |
| 105 | context.Log.Debug("Activity ID:", context.Activity.Id); |
| 106 | ``` |
| 107 | |
| 108 | These delegate to `Microsoft.Extensions.Logging.ILogger` under the hood. The underlying `ILogger` is accessible via `context.Log.Logger` if needed. |
| 109 | |
| 110 | ### Context AppId (BC-5) |
| 111 | |
| 112 | ```csharp |
| 113 | var appId = context.AppId; // reads from TeamsBotApplication.AppId |
| 114 | ``` |
| 115 | |
| 116 | ### WebApplicationBuilder.AddTeams() (BC-7) |
| 117 | |
| 118 | Both styles work: |
| 119 | ```csharp |
| 120 | // Old style (on WebApplicationBuilder) |
| 121 | builder.AddTeams(); |
| 122 | |
| 123 | // New style (on IServiceCollection) |
| 124 | builder.Services.AddTeams(); |
| 125 | ``` |
| 126 | |
| 127 | ### Meeting Handler Aliases (BC-10) |
| 128 | |
| 129 | Both old and new names work: |
| 130 | ```csharp |
| 131 | // Old names |
| 132 | teams.OnMeetingJoin(handler); |
| 133 | teams.OnMeetingLeave(handler); |
| 134 | |
| 135 | // New names (preferred) |
| 136 | teams.OnMeetingParticipantJoin(handler); |
| 137 | teams.OnMeetingParticipantLeave(handler); |
| 138 | ``` |
| 139 | |
| 140 | ### Proactive Messaging (BC-8) |
| 141 | |
| 142 | ```csharp |
| 143 | // Send proactively to a conversation |
| 144 | await teams.Send(conversationId, "Hello!", cancellationToken: ct); |
| 145 | |
| 146 | // Send a threaded reply proactively |
| 147 | await teams.Reply(conversationId, messageId, "Replying!", ct); |
| 148 | ``` |
| 149 | |
| 150 | > **Note:** The service URL is automatically cached from incoming activities. If you need to send proactively before any activity has been received, pass a `serviceUrl` parameter to `Send()`. |
| 151 | |
| 152 | ### InvokeResponse Factory Methods (BC-12) |
| 153 | |
| 154 | ```csharp |
| 155 | // Instead of: new InvokeResponse(200, body) |
| 156 | return InvokeResponse.Ok(body); |
| 157 | |
| 158 | // Typed version |
| 159 | return InvokeResponse.Ok<TaskModuleResponse>(response); |
| 160 | |
| 161 | // Error responses |
| 162 | return InvokeResponse.Error(400, errorDetails); |
| 163 | ``` |
| 164 | |
| 165 | ### MessageActivity Fluent Methods (BC-15) |
| 166 | |
| 167 | Extension methods on `MessageActivity`: |
| 168 | |
| 169 | ```csharp |
| 170 | var msg = new MessageActivity("hello") |
| 171 | .WithSuggestedActions(actions) |
| 172 | .WithAttachmentLayout("carousel") |
| 173 | .AddAttachment(attachment1, attachment2); |
| 174 | ``` |
| 175 | |
| 176 | Available: `WithText()`, `WithSuggestedActions()`, `WithTextFormat()`, `WithAttachmentLayout()`, `AddAttachment()`, `AddStreamFinal()`. |
| 177 | |
| 178 | ### Activity Entity Methods |
| 179 | |
| 180 | Entity getter helpers are exposed via entity-scoped extension methods: |
| 181 | |
| 182 | ```csharp |
| 183 | // Retrieve entity collections |
| 184 | activity.GetMentions(); // IEnumerable<MentionEntity> |
| 185 | activity.GetQuotedMessages(); // IEnumerable<QuotedReplyEntity> (ExperimentalTeamsQuotedReplies) |
| 186 | activity.GetSensitivityLabels(); // IEnumerable<SensitiveUsageEntity> |
| 187 | |
| 188 | // Retrieve single entities |
| 189 | activity.GetClientInfo(); // ClientInfoEntity? |
| 190 | activity.GetCitation(); // CitationEntity? |
| 191 | activity.GetStreamInfo(); // StreamInfoEntity? |
| 192 | activity.GetTargetedMessageInfo(); // TargetedMessageInfoEntity? (ExperimentalTeamsTargeted) |
| 193 | activity.GetProductInfo(); // ProductInfoEntity? |
| 194 | activity.GetMessageEntity(); // OMessageEntity? |
| 195 | ``` |
| 196 | |
| 197 | All Get* methods are extension methods defined in the respective entity files (e.g., `GetMentions` is in `MentionEntityExtensions`). |
| 198 | |
| 199 | --- |
| 200 | |
| 201 | ### App.Builder() Pattern (BC-6) |
| 202 | |
| 203 | `App.Builder()` is supported with `AddOAuth()`: |
| 204 | |
| 205 | ```csharp |
| 206 | // This works in both old and new libraries: |
| 207 | var appBuilder = App.Builder() |
| 208 | .AddOAuth("graph"); |
| 209 | builder.AddTeams(appBuilder); |
| 210 | ``` |
| 211 | |
| 212 | The following `AppBuilder` methods from the old library are **not available** and should use standard ASP.NET DI instead: |
| 213 | |
| 214 | | Old AppBuilder Method | New Equivalent | |
| 215 | |----------------------|----------------| |
| 216 | | `.AddLogger(new ConsoleLogger(...))` | `builder.Logging.AddConsole()` | |
| 217 | | `.AddStorage(storage)` | Register via `builder.Services.AddSingleton<IStorage>(...)` | |
| 218 | | `.AddClient(httpClient)` | Register via `builder.Services.AddHttpClient(...)` | |
| 219 | | `.AddCredentials(credentials)` | Configure in `appsettings.json` AzureAd section | |
| 220 | | `.AddPlugin(plugin)` | No equivalent — plugins are not supported in the new library | |
| 221 | | `.AddCloud(cloud)` | Configure via `appsettings.json` | |
| 222 | |
| 223 | --- |
| 224 | |
| 225 | ## Breaking Changes Requiring Migration |
| 226 | |
| 227 | ### BC-4: `context.Ref` Removed |
| 228 | |
| 229 | **Old:** |
| 230 | ```csharp |
| 231 | var conversationId = context.Ref.Conversation.Id; |
| 232 | ``` |
| 233 | |
| 234 | **New:** |
| 235 | ```csharp |
| 236 | var conversationId = context.Activity.Conversation.Id; |
| 237 | ``` |
| 238 | |
| 239 | The `Ref` property is not available. Use `context.Activity.Conversation` directly — it contains the same data. |
| 240 | |
| 241 | --- |
| 242 | |
| 243 | ### BC-9: `OnSignIn` / `OnSignInFailure` Events |
| 244 | |
| 245 | **Old:** |
| 246 | ```csharp |
| 247 | teams.OnSignIn(async (_, @event, cancellationToken) => { ... }); |
| 248 | teams.OnSignInFailure(async (context, cancellationToken) => { ... }); |
| 249 | ``` |
| 250 | |
| 251 | **New:** |
| 252 | ```csharp |
| 253 | var flow = teams.GetOAuthFlow("graph"); |
| 254 | flow.OnSignInComplete(async (context, token, cancellationToken) => { ... }); |
| 255 | flow.OnSignInFailure(async (context, cancellationToken) => { ... }); |
| 256 | ``` |
| 257 | |
| 258 | Sign-in events are now per-flow callbacks, which is more flexible when using multiple OAuth connections. |
| 259 | |
| 260 | --- |
| 261 | |
| 262 | ### BC-13: Activity Namespace Changes |
| 263 | |
| 264 | | Old Namespace | New Namespace | |
| 265 | |---------------|---------------| |
| 266 | | `Microsoft.Teams.Api.Activities` | `Microsoft.Teams.Apps.Schema` | |
| 267 | | `MessageActivity` | `MessageActivity` (same name) | |
| 268 | | `InvokeActivity` | `InvokeActivity` (same name) | |
| 269 | | `IActivity` | `TeamsActivity` (base class) | |
| 270 | |
| 271 | Member access (`.Text`, `.From`, `.Conversation`, `.Value`, etc.) remains the same. Only `using` statements need updating. |
| 272 | |
| 273 | --- |
| 274 | |
| 275 | ### BC-17: Activity fluent `With*()` methods moved to builder/extensions |
| 276 | |
| 277 | **Old:** |
| 278 | ```csharp |
| 279 | var activity = new Activity().WithFrom(account).WithConversation(conv); |
| 280 | ``` |
| 281 | |
| 282 | **New (recommended builder):** |
| 283 | ```csharp |
| 284 | var activity = new TeamsActivityBuilder() |
| 285 | .WithFrom(account) |
| 286 | .WithConversation(conv) |
| 287 | .Build(); |
| 288 | ``` |
| 289 | |
| 290 | **New (extension methods on `MessageActivity`):** |
| 291 | ```csharp |
| 292 | var activity = new MessageActivity() |
| 293 | .WithFrom(account) |
| 294 | .WithConversation(conv) |
| 295 | .WithChannelId("msteams"); |
| 296 | ``` |
| 297 | |
| 298 | Most base `With*()` methods are available as extension methods in `MessageActivityExtensions` (for example `WithId`, `WithChannelId`, `WithFrom`, `WithRecipient`, `WithConversation`, `WithServiceUrl`, `WithLocale`, `WithTimestamp`, `WithLocalTimestamp`, `WithData`, and `WithAppId`). |
| 299 | |
| 300 | `WithRelatesTo` is still unavailable because there is no `ConversationReference` equivalent in core. |
| 301 | |
| 302 | --- |
| 303 | |
| 304 | ### BC-18: Activity conversion methods replaced by factories |
| 305 | |
| 306 | **Old:** |
| 307 | ```csharp |
| 308 | var msg = activity.ToMessage(); |
| 309 | ``` |
| 310 | |
| 311 | **New:** |
| 312 | ```csharp |
| 313 | var msg = MessageActivity.FromActivity(coreActivity); |
| 314 | ``` |
| 315 | |
| 316 | --- |
| 317 | |
| 318 | ### BC-21: Type incompatibilities |
| 319 | |
| 320 | | Property | Old Type | New Type | |
| 321 | |---|---|---| |
| 322 | | `Timestamp`, `LocalTimestamp` | `DateTime?` | `string?` | |
| 323 | | `ServiceUrl` | `string?` | `Uri?` | |
| 324 | | `ContentUrl`, `ThumbnailUrl` (Attachment) | `string?` | `Uri?` | |
| 325 | | Enums (`TextFormat`, `InputHint`, etc.) | Enum types | String constants | |
| 326 | | `Account` | Custom `Account` class | `ConversationAccount` / `TeamsConversationAccount` | |
| 327 | |
| 328 | --- |
| 329 | |
| 330 | ### Hosting and Plugin Architecture |
| 331 | |
| 332 | The old plugin-based architecture is entirely removed. This affects: |
| 333 | |
| 334 | | Old Pattern | New Equivalent | |
| 335 | |---|---| |
| 336 | | `ISenderPlugin` / `IAspNetCorePlugin` | Not available — use `TeamsBotApplication` directly | |
| 337 | | `AddTeamsPlugin<T>()` | Not available — register services via standard DI | |
| 338 | | `TeamsService` (IHostedService) | Not needed — lifecycle managed by `BotApplication.ProcessAsync()` | |
| 339 | | `AddTeamsTokenAuthentication()` | Built into `AddTeamsBotApplication()` via `BotConfig` | |
| 340 | | `TeamsValidationSettings` | Replaced by `JwtExtensions` + `BotConfig` | |
| 341 | | `AspNetCorePlugin.Configure()` | Use standard `app.UseAuthentication()` / `app.UseAuthorization()` | |
| 342 | |
| 343 | --- |
| 344 | |
| 345 | ### Common library replacements |
| 346 | |
| 347 | | Old Type | New Equivalent | |
| 348 | |---|---| |
| 349 | | `Microsoft.Teams.Common.Logging.ILogger` | `Microsoft.Extensions.Logging.ILogger` | |
| 350 | | `Microsoft.Teams.Common.Logging.ConsoleLogger` | `builder.Logging.AddConsole()` | |
| 351 | | `Microsoft.Teams.Common.Logging.LogLevel` | `Microsoft.Extensions.Logging.LogLevel` | |
| 352 | | `Microsoft.Teams.Common.Http.IHttpClient` | `System.Net.Http.HttpClient` via DI | |
| 353 | | `Microsoft.Teams.Common.Http.IHttpClientFactory` | `Microsoft.Extensions.Http.IHttpClientFactory` | |
| 354 | | `Microsoft.Teams.Common.Http.HttpException` | `System.Net.Http.HttpRequestException` | |
| 355 | | `Microsoft.Teams.Common.Storage.IStorage<K,V>` | No direct replacement — removed from SDK | |
| 356 | | `Microsoft.Teams.Common.Storage.LocalStorage<V>` | No direct replacement — use `IMemoryCache` or custom | |
| 357 | |
| 358 | --- |
| 359 | |
| 360 | ### Testing |
| 361 | |
| 362 | The old `TestPlugin` from `Microsoft.Teams.Apps.Testing` is not available. Use standard .NET testing patterns: |
| 363 | |
| 364 | ```csharp |
| 365 | // Old: TestPlugin-based |
| 366 | var plugin = new TestPlugin(); |
| 367 | var app = App.Builder().AddPlugin(plugin).Build(); |
| 368 | |
| 369 | // New: Direct instantiation with mocks |
| 370 | var mockBot = new Mock<TeamsBotApplication>(...); |
| 371 | var context = new Context<MessageActivity>(mockBot.Object, activity); |
| 372 | ``` |
| 373 | |
| 374 | --- |
| 375 | |
| 376 | ## Items Under Review |
| 377 | |
| 378 | The following items are being evaluated and may change: |
| 379 | |
| 380 | - **BC-1 (partial):** `Send(AdaptiveCard)` / `Reply(AdaptiveCard)` — pending Teams.Cards dependency decision |
| 381 | - **BC-3:** Middleware / `OnActivity` / `Use()` / `Next()` — architecture review needed |
| 382 | - **BC-11:** `OnSetting()` message extension handler — activity type clarification needed |
| 383 | - **BC-14:** `AddTab()` — scope of feature TBD |
| 384 | - **BC-19:** Missing activity types (`TypingActivity`, `EndOfConversationActivity`, `CommandActivity`) |
| 385 | - **BC-20:** Missing handler registration methods (Tab, Command, Infrastructure, commented-out handlers) |
| 386 | - **BC-22:** `Conversation.ToThreadedConversationId()` static utility |
| 387 | - **BC-23:** MessageActivity commented-out properties (`Speak`, `InputHint`, `Summary`, `Importance`, `DeliveryMode`, `Expiration`) |
| 388 | |