Skip to content

Commit 3841b85

Browse files
committed
C#: Basic exercise using Entity Framework Core provider for PostgreSQL
1 parent 9c495e1 commit 3841b85

File tree

8 files changed

+278
-0
lines changed

8 files changed

+278
-0
lines changed

.github/dependabot.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ updates:
3333
schedule:
3434
interval: "daily"
3535

36+
- directory: "/by-language/csharp-efcore"
37+
package-ecosystem: "nuget"
38+
schedule:
39+
interval: "daily"
40+
3641
- directory: "/by-language/csharp-npgsql"
3742
package-ecosystem: "nuget"
3843
schedule:
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
name: C# EF Core
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- '.github/workflows/lang-csharp-efcore.yml'
7+
- 'by-language/csharp-efcore/**'
8+
- '/requirements.txt'
9+
push:
10+
branches: [ main ]
11+
paths:
12+
- '.github/workflows/lang-csharp-efcore.yml'
13+
- 'by-language/csharp-efcore/**'
14+
- '/requirements.txt'
15+
16+
# Allow job to be triggered manually.
17+
workflow_dispatch:
18+
19+
# Run job each night after CrateDB nightly has been published.
20+
schedule:
21+
- cron: '0 3 * * *'
22+
23+
# Cancel in-progress jobs when pushing to the same branch.
24+
concurrency:
25+
cancel-in-progress: true
26+
group: ${{ github.workflow }}-${{ github.ref }}
27+
28+
defaults:
29+
run:
30+
shell: bash
31+
32+
jobs:
33+
test:
34+
name: "
35+
.NET: ${{ matrix.dotnet-version }}
36+
CrateDB: ${{ matrix.cratedb-version }}
37+
on ${{ matrix.os }}"
38+
runs-on: ${{ matrix.os }}
39+
strategy:
40+
fail-fast: false
41+
matrix:
42+
os: [ 'ubuntu-22.04' ]
43+
dotnet-version: [ '9.0.x' ]
44+
cratedb-version: [ 'nightly' ]
45+
46+
# https://docs.github.com/en/free-pro-team@latest/actions/guides/about-service-containers
47+
services:
48+
cratedb:
49+
image: crate/crate:${{ matrix.cratedb-version }}
50+
ports:
51+
- 4200:4200
52+
- 5432:5432
53+
env:
54+
CRATE_HEAP_SIZE: 4g
55+
56+
steps:
57+
58+
- name: Acquire sources
59+
uses: actions/checkout@v4
60+
61+
- name: Set up .NET ${{ matrix.dotnet-version }}
62+
uses: actions/setup-dotnet@v4
63+
with:
64+
dotnet-version: ${{ matrix.dotnet-version }}
65+
66+
- name: Set up Python
67+
uses: actions/setup-python@v5
68+
with:
69+
python-version: "3.13"
70+
architecture: x64
71+
cache: 'pip'
72+
cache-dependency-path: |
73+
requirements.txt
74+
75+
- name: Install utilities
76+
run: |
77+
pip install -r requirements.txt
78+
79+
- name: Validate by-language/csharp-efcore
80+
run: |
81+
ngr test by-language/csharp-efcore --dotnet-version=${{ matrix.dotnet-version }}

by-language/csharp-efcore/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*.user
2+
bin
3+
obj
4+
TestResults

by-language/csharp-efcore/BlogDemo.cs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Example program for EFCore with CrateDB.
2+
3+
// Npgsql Entity Framework Core provider for PostgreSQL
4+
// https://github.com/npgsql/efcore.pg
5+
using System;
6+
using System.Linq;
7+
using Microsoft.EntityFrameworkCore;
8+
9+
public class Blog
10+
{
11+
public long Id { get; set; }
12+
public required string Name { get; set; }
13+
}
14+
15+
public class BloggingContext : DbContext
16+
{
17+
public DbSet<Blog> Blogs { get; set; }
18+
19+
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
20+
=> optionsBuilder.UseNpgsql(@"Host=localhost;Username=crate;Password=;Database=testdrive");
21+
}
22+
23+
class BlogDemo
24+
{
25+
public BloggingContext context;
26+
27+
public static void Main(string[] args)
28+
{
29+
var blogDemo = new BlogDemo();
30+
blogDemo.run();
31+
}
32+
33+
public BlogDemo()
34+
{
35+
// Provide context and turn off transactions.
36+
context = new BloggingContext();
37+
context.Database.AutoTransactionBehavior = AutoTransactionBehavior.Always;
38+
context.Database.AutoSavepointsEnabled = false;
39+
40+
// CrateDB does not implement 'DROP DATABASE'.
41+
// await context.Database.EnsureDeleted();
42+
context.Database.EnsureCreated();
43+
}
44+
45+
public void run()
46+
{
47+
RunDDL();
48+
EfCoreWorkload();
49+
}
50+
51+
public void RunDDL()
52+
{
53+
context.Database.ExecuteSqlRaw("""
54+
CREATE TABLE IF NOT EXISTS "Blogs"
55+
("Id" bigint GENERATED ALWAYS AS NOW(), "Name" text);
56+
""");
57+
}
58+
59+
public void EfCoreWorkload()
60+
{
61+
62+
// Insert a Blog.
63+
context.Blogs.Add(new() { Name = "FooBlog" });
64+
context.SaveChanges();
65+
66+
// For converging written data immediately, submit a cluster-wide flush operation.
67+
// https://cratedb.com/docs/crate/reference/en/latest/sql/statements/refresh.html#sql-refresh
68+
context.Database.ExecuteSqlRaw("""REFRESH TABLE "Blogs";""");
69+
70+
// Query all blogs whose name starts with F.
71+
var fBlogs = context.Blogs.Where(b => b.Name.StartsWith("F")).ToList();
72+
73+
// Output the results to the console.
74+
foreach (var blog in fBlogs)
75+
{
76+
Console.WriteLine($"Blog Id: {blog.Id}, Blog Name: {blog.Name}");
77+
}
78+
}
79+
}

by-language/csharp-efcore/README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Npgsql Entity Framework Core provider for PostgreSQL with CrateDB
2+
3+
## About
4+
5+
The file `BlogDemo.cs` includes a demonstration program written in [C#] which
6+
demonstrates the [Entity Framework Core] README code snippet of [efcore.pg],
7+
effectively a very basic usage scenario, with CrateDB.
8+
9+
`efcore.pg` is built on top of [Npgsql - .NET Access to PostgreSQL].
10+
11+
## Usage
12+
13+
Start a CrateDB instance for evaluation purposes.
14+
```shell
15+
docker run -it --rm --publish=4200:4200 --publish=5432:5432 crate:latest
16+
```
17+
18+
Invoke example program.
19+
```shell
20+
dotnet run
21+
```
22+
23+
## Tests
24+
25+
Invoke software tests.
26+
```shell
27+
dotnet test
28+
```
29+
30+
Generate a Cobertura code coverage report.
31+
```shell
32+
dotnet test --collect:"XPlat Code Coverage"
33+
```
34+
35+
Invoke test cases selectively.
36+
```shell
37+
dotnet test --filter BlogDemoTest
38+
```
39+
40+
41+
[C#]: https://en.wikipedia.org/wiki/C_Sharp_(programming_language)
42+
[efcore.pg]: https://github.com/npgsql/efcore.pg
43+
[Entity Framework Core]: https://learn.microsoft.com/en-gb/ef/core/
44+
[Npgsql - .NET Access to PostgreSQL]: https://github.com/npgsql/npgsql

by-language/csharp-efcore/backlog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Backlog for Entity Framework Core for PostgreSQL with CrateDB
2+
3+
- Provide test rig for all data types of CrateDB, like `DemoTypes.cs` of `csharp-npgsql`.
4+
- Can DDL statements be derived from the model, and automatically be applied?
5+
- Can models be derived by introspecting the database?

by-language/csharp-efcore/demo.csproj

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net$(NETCoreAppMaximumVersion)</TargetFramework>
6+
<Nullable>enable</Nullable>
7+
<IsPackable>false</IsPackable>
8+
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
9+
<GenerateProgramFile>false</GenerateProgramFile>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
14+
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
15+
<PackageReference Include="xunit" Version="2.9.3" />
16+
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2" />
17+
<PackageReference Include="coverlet.collector" Version="6.0.4" />
18+
</ItemGroup>
19+
20+
</Project>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System.Data;
2+
using Microsoft.EntityFrameworkCore;
3+
using Npgsql;
4+
using Xunit;
5+
6+
public class BlogDemoTest
7+
{
8+
public NpgsqlConnection GetConnection(string? connectionString)
9+
{
10+
var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString);
11+
// dataSourceBuilder.EnableDynamicJson();
12+
using var dataSource = dataSourceBuilder.Build();
13+
return dataSource.OpenConnection();
14+
}
15+
16+
[Fact]
17+
public void TestBlogDemo()
18+
{
19+
// Invoke example database workload.
20+
var blogDemo = new BlogDemo();
21+
blogDemo.run();
22+
23+
// Validate database content.
24+
var conn = GetConnection(blogDemo.context.Database.GetConnectionString());
25+
var cmd = new NpgsqlCommand("""SELECT * FROM testdrive."Blogs";""", conn);
26+
var reader = cmd.ExecuteReader();
27+
28+
var data = new DataTable();
29+
data.Load(reader);
30+
31+
// Check first record.
32+
var row = data.Rows[0];
33+
Assert.NotNull(row["Id"]);
34+
Assert.Equal("FooBlog", row["Name"]);
35+
36+
conn.Close();
37+
conn.Dispose();
38+
}
39+
40+
}

0 commit comments

Comments
 (0)