Skip to content

Activity specific builders for outgoing activities#392

Open
MehakBindra wants to merge 1 commit intonext/corefrom
next/core-activitybuilder
Open

Activity specific builders for outgoing activities#392
MehakBindra wants to merge 1 commit intonext/corefrom
next/core-activitybuilder

Conversation

@MehakBindra
Copy link
Copy Markdown
Collaborator

@MehakBindra MehakBindra commented Mar 26, 2026

What changed: Introduced type-specific activity builders alongside the existing TeamsActivityBuilder.
New types

  • TeamsActivityBuilder<TActivity, TBuilder> — abstract generic base that all Teams builders inherit from. Holds shared methods: WithChannelData, WithEntities, AddEntity, WithAttachments, AddAttachment, AddAdaptiveCardAttachment, and the Teams-specific overrides for SetConversation/SetFrom/SetRecipient.
  • MessageActivityBuilder — concrete builder for MessageActivity with typed setters for WithText, WithSuggestedActions, and AddMention (reads/writes activity.Text directly).
  • StreamingActivityBuilder — concrete builder for StreamingActivity with typed WithText.
  • TeamsActivityBuilder — now just a thin concrete class with Build() only, backwards-compatible for non-message use cases.

Why

  • Previously TeamsActivityBuilder built all activity types but returned TeamsActivity. So you could do WithText("...") but not access activity.Text after building. This is a problem. Even if we internally cast, the user cannot access activity.Text without casting again
    With dedicated builders, MessageActivity.CreateBuilder().WithText(...).Build() returns a MessageActivity — activity.Text just works. Same for SuggestedActions, TextFormat, etc.
  • Since we have limited outgoing activities, this is a clean approach. Developers should directly be accessing Message Activity, and Teams Activity is more of an internal class to coordinate shared properties across outgoing/incoming activities and a bridge to core activity.

Ripple changes

  • CoreActivityBuilder.WithConversationReference parameter widened from TActivity to CoreActivity so any builder can accept any activity as a conversation reference source.
  • Context.cs and all samples updated to use MessageActivity.CreateBuilder() where they were building message replies.
  • Test suite split into TeamsActivityBuilderTests, MessageActivityBuilderTests, StreamingActivityBuilderTests to match the new structure.

@MehakBindra MehakBindra marked this pull request as draft March 26, 2026 00:44
Copy link
Copy Markdown
Collaborator

@singhk97 singhk97 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me

/// <typeparam name="TBuilder">The concrete builder type (for fluent chaining).</typeparam>
public abstract class TeamsActivityBuilder<TActivity, TBuilder> : CoreActivityBuilder<TActivity, TBuilder>
where TActivity : TeamsActivity
where TBuilder : TeamsActivityBuilder<TActivity, TBuilder>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this recursive TBuilder definition work?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's called CRTP pattern , or in our specific case Fluent builder inheritance or self-referential generics

it's a bit of a workaround for the lack of a Self type

if you are interested here it is a very related discussion dotnet/csharplang#7325

@MehakBindra MehakBindra marked this pull request as ready for review March 26, 2026 19:29
@MehakBindra MehakBindra changed the title activity specific builders Activity specific builders Mar 26, 2026
@MehakBindra MehakBindra changed the title Activity specific builders Activity specific builders for outgoing activities Mar 26, 2026

MessageActivityBuilder result1 = msgBuilder.WithId("id");
MessageActivityBuilder result2 = msgBuilder.WithText("text");
MessageActivityBuilder result3 = msgBuilder.WithType(TeamsActivityType.Message);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can we hide WithType to avoid this usage?

var msg = MessageActivity.CreateBuilder()
  .WithType("not-message")
  .Build()

Eg:

  1. Make CoreMessageBuilder.WithType virtual
  2. Override in MessageActivityBuilder and check the valid values for "type" to throw in the other cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants