Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write up documentation on posthog-dotnet #10589

Merged
merged 26 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4f3c5e8
Write up documentation on posthog-dotnet
haacked Feb 6, 2025
f02dc6e
Link to the correct snippet
haacked Feb 6, 2025
46c26c9
Format code and replace python with dotnet
haacked Feb 6, 2025
78b2e12
Remove python code
haacked Feb 6, 2025
c682366
Add C# code sample
haacked Feb 6, 2025
fc9e314
Add .NET to the sidebar
haacked Feb 6, 2025
0984538
Format code to fit
haacked Feb 6, 2025
d6ed75d
Add C# syntax highlighting
haacked Feb 6, 2025
ff0f0df
Update method names for .NET
haacked Feb 6, 2025
4fd8557
Update contents/docs/integrate/feature-flags-code/_snippets/feature-f…
haacked Feb 7, 2025
930fd63
Update contents/docs/libraries/dotnet/index.mdx
haacked Feb 7, 2025
76d7ee0
Update contents/docs/libraries/dotnet/index.mdx
haacked Feb 7, 2025
968a0e4
Update contents/docs/libraries/dotnet/index.mdx
haacked Feb 7, 2025
71b7441
Update contents/docs/libraries/dotnet/index.mdx
haacked Feb 7, 2025
64d7adc
Update contents/docs/libraries/dotnet/index.mdx
haacked Feb 7, 2025
cd25ee7
Update contents/docs/integrate/_snippets/install-dotnet.mdx
haacked Feb 7, 2025
a2b0924
Update contents/docs/integrate/send-events/_snippets/send-events-dotn…
haacked Feb 7, 2025
8a1249f
Merge branch 'master' into haacked/dotnet-library
haacked Feb 10, 2025
2b8e127
Add .NET to the tabs list
haacked Feb 10, 2025
a45dda0
Add .NET to the tabs list
haacked Feb 10, 2025
806eab2
Add capture events docs
haacked Feb 10, 2025
3dc579a
Add .NET to `adding-feature-flag-code`
haacked Feb 10, 2025
de25e3e
Add .NET to experiments/installation
haacked Feb 10, 2025
af47c22
Add .NET to adding experiment code
haacked Feb 10, 2025
422a063
Add .NET logo
haacked Feb 10, 2025
703b97d
Add .NET to the list of server-side libraries
haacked Feb 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,16 @@ posthog.capture(
);
```

```csharp
posthog.Capture(
"distinct_id",
"event_name_of_your_goal_metric",
properties: new() {
["$feature/experiment-feature-flag-key"] = "variant-name"
}
);
```

</MultiLanguage>

## Step 4 (optional): Send the `$feature_flag_called` event
Expand Down Expand Up @@ -245,4 +255,15 @@ posthog.capture(
);
```

```csharp
posthog.Capture(
"distinct_id",
"$feature_flag_called",
properties: new() {
["$feature_flag_response"] = "variant-name",
["feature_flag"] = "feature-flag-key"
}
);
```

</MultiLanguage>
5 changes: 5 additions & 0 deletions contents/docs/feature-flags/installation.mdx
haacked marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import JavaInstall from '../integrate/_snippets/install-java.mdx'
import RustInstall from '../integrate/_snippets/install-rust.mdx'
import FlutterInstall from '../integrate/_snippets/install-flutter.mdx'
import ElixirInstall from '../integrate/_snippets/install-elixir.mdx'
import DotNetInstall from '../integrate/_snippets/install-dotnet.mdx'

<!-- prettier-ignore -->
<Tab.Group tabs={['Web', 'React', 'Node.js', 'Python', 'PHP', 'Ruby', 'Go', 'React Native', 'Android', 'iOS', 'Java', 'Rust', 'Elixir', 'Flutter', 'api']}>
Expand All @@ -49,6 +50,7 @@ import ElixirInstall from '../integrate/_snippets/install-elixir.mdx'
<Tab>Rust</Tab>
<Tab>Elixir</Tab>
<Tab>Flutter</Tab>
<Tab>.NET</Tab>
<Tab>API</Tab>
</Tab.List>
<Tab.Panels>
Expand Down Expand Up @@ -100,6 +102,9 @@ import ElixirInstall from '../integrate/_snippets/install-elixir.mdx'
<Tab.Panel>
<FlutterInstall />
</Tab.Panel>
<Tab.Panel>
<DotNetInstall />
</Tab.Panel>
<Tab.Panel>
<APIInstall />
<blockquote>
Expand Down
55 changes: 55 additions & 0 deletions contents/docs/integrate/_snippets/install-dotnet.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<DotNetInstall />

At the moment, ASP.NET Core is the only supported platform for the PostHog .NET SDK.

```bash
dotnet add package PostHog.AspNetCore
```

In your `Program.cs` file, add the following code:

```csharp
using PostHog.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

builder.AddPostHog();
```

Make sure to configure PostHog with your project API key, instance address, and optional personal API key. For example, in appsettings.json:
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Make sure to configure PostHog with your project API key, instance address, and optional personal API key. For example, in appsettings.json:
Make sure to configure PostHog with your project API key, API host, and optional personal API key (which you can get from [your project settings](https://us.posthog.com/settings/project)). For example, in `appsettings.json`:


```json
{
"PostHog": {
"ProjectApiKey": "phc_...",
"Host": "https://us.i.posthog.com"
Comment on lines +24 to +25
Copy link
Contributor

Choose a reason for hiding this comment

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

These will automatically fill with values if they exist

Suggested change
"ProjectApiKey": "phc_...",
"Host": "https://us.i.posthog.com"
"ProjectApiKey": "<ph_project_api_key>",
"Host": "<ph_client_api_host>"

}
}
```

> **Note:** If the host is not specified, the default host `https://us.i.posthog.com` is used.

Use a secrets manager to store your personal API key. For example, when developing locally you can use the `UserSecrets` feature of the `dotnet` CLI:
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Use a secrets manager to store your personal API key. For example, when developing locally you can use the `UserSecrets` feature of the `dotnet` CLI:
Adding a personal API key is optional, but enables you to [locally evaluate feature flags](/docs/feature-flags/local-evaluation). Use a secrets manager to store your personal API key. For example, when developing locally you can use the `UserSecrets` feature of the `dotnet` CLI:


```bash
dotnet user-secrets init
dotnet user-secrets set "PostHog:PersonalApiKey" "phc_..."
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
dotnet user-secrets set "PostHog:PersonalApiKey" "phc_..."
dotnet user-secrets set "PostHog:PersonalApiKey" "phx_..."

```

You can find your project API key and instance address in the [project settings](https://app.posthog.com/project/settings) page in PostHog.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
You can find your project API key and instance address in the [project settings](https://app.posthog.com/project/settings) page in PostHog.


To see detailed logging, set the log level to `Debug` or `Trace` in appsettings.json:
haacked marked this conversation as resolved.
Show resolved Hide resolved

```json
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"PostHog": "Trace"
}
},
...
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
The `Capture()` method has an optional argument `sendFeatureFlags`, which is set to `false` by default. By setting this to `true`, feature flag information will automatically be sent with the event.

Note that by doing this, PostHog will make an additional request to fetch feature flag information before capturing the event. So this method is only recommended if you don't mind the extra API call and delay.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Note that by doing this, PostHog will make an additional request to fetch feature flag information before capturing the event. So this method is only recommended if you don't mind the extra API call and delay.
By doing this, PostHog will make an additional request to fetch feature flag information before capturing the event so this method is only recommended if you don't mind the extra API call and delay.


```csharp
posthog.Capture(
"distinct_id_of_your_user",
"event_name",
properties: null,
groups: null,
sendFeatureFlags: true
);
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
There are 2 steps to implement feature flags in .NET:

### Step 1: Evaluate the feature flag value

#### Boolean feature flags

```csharp
var isMyFlagEnabled = await posthog.IsFeatureEnabledAsync(
"flag-key",
"distinct_id_of_your_user"
);
if (isMyFlagEnabled.GetValueOrDefault())
{
// Feature is enabled
}
else
{
// Feature is disabled
}
```

**Note:** The `IsFeatureEnabledAsync` method returns a nullable boolean. If the flag is not found or evaluating it is inconclusive, it returns `null`.
haacked marked this conversation as resolved.
Show resolved Hide resolved

#### Multivariate feature flags

```csharp
var flag = await posthog.GetFeatureFlagAsync(
"flag-key",
"distinct_id_of_your_user"
);

// replace "variant-key" with the key of your variant
if (flag is { VariantKey: "variant-key"} ) {
// Do something differently for this user
// Optional: fetch the payload
var matchedPayload = flag.Payload;
}
```

The `GetFeatureFlagAsync` method returns a nullable `FeatureFlag` object. If the flag is not found or evaluating it is inconclusive, it returns `null`. However, there is an implicit conversion to bool to make comparisons easier.

```csharp
if (await posthog.GetFeatureFlagAsync(
"flag-key",
"distinct_id_of_your_user")
)
{
// Do something differently for this user
}
```

import IncludePropertyInEvents from "./include-feature-flag-property-in-backend-events.mdx"

<IncludePropertyInEvents />

There are two methods you can use to include feature flag information in your events:

#### Method 1: Include the `$feature/feature_flag_name` property

In the event properties, include `$feature/feature_flag_name: variant_key`:
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
In the event properties, include `$feature/feature_flag_name: variant_key`:
In the event properties, include `$feature/feature_flag_name: variant_key`:


```csharp
posthog.Capture(
"distinct_id_of_your_user",
"event_name",
properties: new() {
// replace feature-flag-key with your flag key.
// Replace 'variant-key' with the key of your variant
["$feature/feature-flag-key"] = "variant-key"
}
);
```

#### Method 2: Set `send_feature_flags` to `true`

import DotNetSetSendFeatureFlagsTrue from "./feature-flags-code-dotnet-set-send-feature-flags-to-true.mdx"

<DotNetSetSendFeatureFlagsTrue />

### Fetching all flags for a user

You can fetch all flag values for a single user by calling `GetAllFeatureFlagsAsync()`.

This is useful when you need to fetch multiple flag values and don't want to make multiple requests.

```csharp
var flags = await posthog.GetAllFeatureFlagsAsync(
"distinct_id_of_your_user"
);
```

### Sending `$feature_flag_called` events

Capturing `$feature_flag_called` events enable PostHog to know when a flag was accessed by a user and thus provide [analytics and insights](/docs/product-analytics/insights) on the flag. By default, we send a these event when:

1. You call `posthog.GetFeatureFlagAsync()` or `posthog.IsFeatureEnabledAsync()`, AND
2. It's a new user, or the value of the flag has changed.

> *Note:* Tracking whether it's a new user or if a flag value has changed happens in a local cache. This means that if you reinitialize the PostHog client, the cache resets as well – causing `$feature_flag_called` events to be sent again when calling `GetFeatureFlagAsync` or `IsFeatureEnabledAsync`. PostHog is built to handle this, and so duplicate `$feature_flag_called` events won't affect your analytics.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
> *Note:* Tracking whether it's a new user or if a flag value has changed happens in a local cache. This means that if you reinitialize the PostHog client, the cache resets as well – causing `$feature_flag_called` events to be sent again when calling `GetFeatureFlagAsync` or `IsFeatureEnabledAsync`. PostHog is built to handle this, and so duplicate `$feature_flag_called` events won't affect your analytics.
> **Note:** Tracking whether it's a new user or if a flag value has changed happens in a local cache. This means that if you reinitialize the PostHog client, the cache resets as well – causing `$feature_flag_called` events to be sent again when calling `GetFeatureFlagAsync` or `IsFeatureEnabledAsync`. PostHog is built to handle this, and so duplicate `$feature_flag_called` events won't affect your analytics.


You can disable automatically the additional request to capture `$feature_flag_called` events. For example, when you don't need the analytics, or it's being called at such a high volume that sending events slows things down.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
You can disable automatically the additional request to capture `$feature_flag_called` events. For example, when you don't need the analytics, or it's being called at such a high volume that sending events slows things down.
You can disable automatic capture of these events and manually capture `$feature_flag_called` events instead. This is helpful when you don't need the analytics, or it's being called at such a high volume that sending events slows things down.


To disable it, set the `sendFeatureFlagsEvent` option in your function call, like so:

```csharp
var isMyFlagEnabled = await posthog.IsFeatureEnabledAsync(
"flag-key",
"distinct_id_of_your_user",
options: new FeatureFlagOptions
{
SendFeatureFlagEvents = true
}
);
// will not send `$feature_flag_called` events
Comment on lines +111 to +114
Copy link
Contributor

Choose a reason for hiding this comment

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

should this be false?

```

import DotNetOverrideServerProperties from './override-server-properties/dotnet.mdx'

<DotNetOverrideServerProperties />
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import OverrideServerPropertiesIntro from './override-server-properties-intro.mdx'

<OverrideServerPropertiesIntro />

```csharp
var flag = await posthog.GetFeatureFlagAsync(
"flag-key",
"distinct_id_of_the_user",
options: new FeatureFlagOptions
{
PersonProperties = new() { ["property_name"] = "value" },
Groups = [
new Group("your_group_type", "your_group_id")
{
["group_property_name"] = "your group value"
},
new Group(
"another_group_type",
"another_group_id",
new Dictionary<string, object?>
{
["group_property_name"] = "another group value"
}
)
]
});
```

import OverrideGeoIPPropertiesSDK from './override-geoip-properties-SDKs.mdx'

<OverrideGeoIPPropertiesSDK />
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import SettingProperties from "./setting-properties-text.mdx"
import NamingTip from "./naming-tip.mdx"
import Intro from "./intro.mdx"

<Intro />

```csharp
posthog.Capture("distinct_id_of_the_user", "user_signed_up");
```

<NamingTip />

<SettingProperties />

```csharp
posthog.Capture(
"distinct_id_of_the_user",
"user_signed_up",
properties: new() {
["login_type"] = "email",
["is_free_trial"] = "true"
}
);
```

### Sending page views

If you're aiming for a backend-only implementation of PostHog and won't be capturing events from your frontend, you can send `pageviews` from your backend like so:
haacked marked this conversation as resolved.
Show resolved Hide resolved

```csharp
using PostHog;
using Microsoft.AspNetCore.Http.Extensions;

posthog.CapturePageView(
"distinct_id_of_the_user",
context.HttpContext.Request.GetDisplayUrl());
```
10 changes: 10 additions & 0 deletions contents/docs/libraries/dotnet/_snippets/capture.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
```csharp
posthog.Capture(
distinctId: "distinct_id",
eventName: "movie_played",
properties: new()
{
["movie_id"] = 123,
["category"] = "romcom",
});
```
7 changes: 7 additions & 0 deletions contents/docs/libraries/dotnet/_snippets/identify.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```csharp
await posthog.IdentifyAsync(
"distinct_id_of_the_user",
email: "[email protected]",
name: "John Doe"
);
```
Loading