NuVatis.Extensions.EntityFrameworkCore
1.0.4
See the version list below for details.
dotnet add package NuVatis.Extensions.EntityFrameworkCore --version 1.0.4
NuGet\Install-Package NuVatis.Extensions.EntityFrameworkCore -Version 1.0.4
<PackageReference Include="NuVatis.Extensions.EntityFrameworkCore" Version="1.0.4" />
<PackageVersion Include="NuVatis.Extensions.EntityFrameworkCore" Version="1.0.4" />
<PackageReference Include="NuVatis.Extensions.EntityFrameworkCore" />
paket add NuVatis.Extensions.EntityFrameworkCore --version 1.0.4
#r "nuget: NuVatis.Extensions.EntityFrameworkCore, 1.0.4"
#:package NuVatis.Extensions.EntityFrameworkCore@1.0.4
#addin nuget:?package=NuVatis.Extensions.EntityFrameworkCore&version=1.0.4
#tool nuget:?package=NuVatis.Extensions.EntityFrameworkCore&version=1.0.4
NuVatis
MyBatis-style SQL Mapper for .NET, powered by Roslyn Source Generators.
Overview
NuVatis는 Entity Framework의 성능 오버헤드와 인라인 SQL의 유지보수성 문제를 동시에 해결하는 SQL Mapper 프레임워크다.
- SQL은 XML 또는 C# Attribute로 별도 관리
- Roslyn Source Generator가 빌드타임에 매핑 코드를 자동 생성
- 런타임 리플렉션 제로, Native AOT 호환 (.NET 8)
- ADO.NET 기반 최소 추상화, 최대 성능
- .NET 7 / .NET 8 동시 지원 (멀티 타겟)
Packages
| Package | Description |
|---|---|
NuVatis.Core |
핵심 런타임 (Session, Executor, Transaction, Mapping, Cache) |
NuVatis.Generators |
Roslyn Source Generator (XML 파싱, 분석, 코드 생성) |
NuVatis.PostgreSql |
PostgreSQL Provider (Npgsql) |
NuVatis.MySql |
MySQL/MariaDB Provider (MySqlConnector) |
NuVatis.SqlServer |
SQL Server Provider (Microsoft.Data.SqlClient) |
NuVatis.Sqlite |
SQLite Provider (Microsoft.Data.Sqlite) |
NuVatis.Extensions.DependencyInjection |
Microsoft DI 통합 + Health Check |
NuVatis.Extensions.OpenTelemetry |
OpenTelemetry 분산 추적 (ActivitySource) |
NuVatis.Extensions.EntityFrameworkCore |
EF Core DbContext 커넥션/트랜잭션 공유 |
NuVatis.Extensions.Aspire |
.NET Aspire 통합 (Health Check + OTel 자동 구성) |
NuVatis.Testing |
테스트 지원 (InMemorySqlSession, QueryCapture) |
Quick Start
Installation
dotnet add package NuVatis.Core
dotnet add package NuVatis.Generators
dotnet add package NuVatis.PostgreSql
dotnet add package NuVatis.Extensions.DependencyInjection
Mapper Interface
public interface IUserMapper {
User? GetById(int id);
Task<User?> GetByIdAsync(int id, CancellationToken ct = default);
IList<User> Search(UserSearchParam param);
int Insert(User user);
int Update(User user);
int Delete(int id);
}
XML Mapper
<?xml version="1.0" encoding="utf-8" ?>
<mapper namespace="Sample.Mappers.IUserMapper">
<cache eviction="LRU" flushInterval="600000" size="512" />
<resultMap id="UserResult" type="User">
<id column="id" property="Id" />
<result column="user_name" property="UserName" />
<result column="email" property="Email" />
</resultMap>
<select id="GetById" resultMap="UserResult">
SELECT id, user_name, email FROM users WHERE id = #{Id}
</select>
<select id="Search" resultMap="UserResult">
SELECT id, user_name, email FROM users
<where>
<if test="UserName != null">
AND user_name LIKE #{UserName}
</if>
<foreach collection="Ids" item="id"
open="AND id IN (" separator="," close=")">
#{id}
</foreach>
</where>
</select>
<insert id="Insert">
INSERT INTO users (user_name, email) VALUES (#{UserName}, #{Email})
</insert>
</mapper>
C# Attribute (Static SQL)
public interface IUserMapper {
[Select("SELECT id, user_name, email FROM users WHERE id = #{Id}")]
[ResultMap("UserResult")]
User? GetById(int id);
[Insert("INSERT INTO users (user_name, email) VALUES (#{UserName}, #{Email})")]
int Insert(User user);
}
DI Integration (ASP.NET Core)
builder.Services.AddNuVatis(options => {
options.ConnectionString = builder.Configuration.GetConnectionString("Default");
options.Provider = new PostgreSqlProvider();
options.RegisterMappers(NuVatisMapperRegistry.RegisterAll);
options.RegisterAttributeStatements(NuVatisMapperRegistry.RegisterAttributeStatements);
});
builder.Services.AddHealthChecks().AddNuVatis();
Usage
public class UserService {
private readonly IUserMapper _mapper;
public UserService(IUserMapper mapper) {
_mapper = mapper;
}
public async Task<User?> GetUser(int id) {
return await _mapper.GetByIdAsync(id);
}
}
Manual Session (Non-DI)
using var session = factory.OpenSession();
var mapper = session.GetMapper<IUserMapper>();
var user = mapper.GetById(1);
mapper.Insert(newUser);
session.Commit();
Session Lifecycle
| Environment | Registration | Lifecycle |
|---|---|---|
| ASP.NET Core (DI) | Scoped | Per HTTP request |
| Generic Host (DI) | Scoped | Per scope |
| Console/Batch | Manual | using block |
autoCommit: false(default) - MyBatis 호환. Commit 없이 Dispose 시 자동 RollbackautoCommit: true- 각 쿼리 후 즉시 커밋- Lazy Connection - 첫 쿼리 시점에 DB 연결 개시
Transaction Management
using var session = factory.OpenSession();
var mapper = session.GetMapper<IUserMapper>();
mapper.Insert(user);
mapper.Insert(order);
session.Commit();
ExecuteInTransactionAsync:
await session.ExecuteInTransactionAsync(async () => {
await mapper.InsertAsync(user);
await mapper.InsertAsync(order);
});
Streaming (IAsyncEnumerable)
대용량 결과를 메모리에 모두 적재하지 않고 스트리밍으로 소비한다.
await foreach (var row in session.SelectStream<StatRow>("Stats.GetAll")) {
Process(row);
}
Multi-ResultSet
하나의 SQL에서 반환되는 여러 결과셋을 순차 소비한다.
await using var results = await session.SelectMultipleAsync("Dashboard.Overview", param);
var summary = await results.ReadAsync<DashboardSummary>();
var details = await results.ReadListAsync<DashboardDetail>();
var trends = await results.ReadListAsync<TrendData>();
Second-Level Cache
Namespace 단위 LRU 캐시. Select 시 캐시 히트하면 DB를 건너뛴다. Insert/Update/Delete 실행 시 해당 namespace 캐시를 자동 무효화한다.
XML 설정:
<cache eviction="LRU" flushInterval="600000" size="512" />
<select id="GetMonthlyStats" resultMap="StatsResult" useCache="true">
SELECT ... FROM monthly_stats WHERE month = #{Month}
</select>
ICacheProvider 인터페이스를 통해 Redis 등 외부 캐시로 교체 가능하다.
EF Core Integration
EF Core와 동일 트랜잭션 내에서 NuVatis 쿼리를 실행한다. DbConnection/DbTransaction을 자동 공유한다.
builder.Services.AddNuVatis(options => { ... });
builder.Services.AddNuVatisEntityFrameworkCore<AppDbContext>();
수동 사용:
await using var nuvatisSession = await dbContext.OpenNuVatisSessionAsync(factory);
var stats = await nuvatisSession.SelectListAsync<MonthlyStats>("Stats.GetMonthly");
Interceptors
SQL 실행 전후에 횡단 관심사를 처리한다.
Prometheus Metrics (내장)
factory.AddInterceptor(new MetricsInterceptor());
Meter "NuVatis": nuvatis.query.total, nuvatis.query.duration, nuvatis.query.errors.total
OpenTelemetry Tracing
factory.AddInterceptor(new OpenTelemetryInterceptor());
ActivitySource "NuVatis.SqlSession" 기반 분산 추적. 태그: db.system, db.statement, db.operation, otel.status_code
Health Check (ASP.NET Core)
builder.Services.AddHealthChecks().AddNuVatis();
SELECT 1 핑 쿼리로 DB 연결 상태를 검증한다. __nuvatis_health Statement가 자동 등록된다.
CommandTimeout
Statement 단위로 SQL 실행 타임아웃을 설정할 수 있다. 우선순위: Statement > Session Default.
<select id="HeavyReport" commandTimeout="120">
SELECT ... FROM large_table ...
</select>
Dynamic SQL Tags
| Tag | Description |
|---|---|
<if test="..."> |
조건부 SQL |
<choose>/<when>/<otherwise> |
Switch-case |
<where> |
자동 WHERE 절 처리 |
<set> |
자동 SET 절 처리 |
<foreach> |
컬렉션 반복 |
<bind> |
변수 바인딩 (OGNL 표현식) |
<sql>/<include> |
SQL 프래그먼트 재사용 |
External Connection Sharing
외부에서 관리하는 DbConnection/DbTransaction을 NuVatis에서 사용할 수 있다. 커넥션/트랜잭션 수명 관리는 외부 호출자에 위임된다.
using var session = factory.FromExistingConnection(connection, transaction);
var data = session.SelectList<Item>("Items.GetAll");
Testing
var session = new InMemorySqlSession();
session.Setup("UserMapper.GetById", expectedUser);
var result = session.SelectOne<User>("UserMapper.GetById");
Assert.True(QueryCapture.HasQuery(session, "UserMapper.GetById"));
Assert.Equal(1, QueryCapture.QueryCount(session, "UserMapper.GetById"));
Custom DB Provider
[NuVatisProvider("CustomDb")]
public class CustomDbProvider : IDbProvider {
public string Name => "CustomDb";
public DbConnection CreateConnection(string connectionString)
=> new CustomDbConnection(connectionString);
public string ParameterPrefix => "@";
public string GetParameterName(int index) => $"@p{index}";
public string WrapIdentifier(string name) => $"\"{name}\"";
}
XML Schema Validation
NuVatis.Core 패키지에 XML 스키마 파일이 포함되어 있다. IDE에서 XML 매퍼 작성 시 자동완성 및 유효성 검사에 활용할 수 있다.
<?xml version="1.0" encoding="utf-8" ?>
<mapper namespace="..."
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="schemas/nuvatis-mapper.xsd">
| Schema | Description |
|---|---|
schemas/nuvatis-mapper.xsd |
Mapper XML 스키마 (select, insert, update, delete, resultMap, cache, dynamic SQL) |
schemas/nuvatis-config.xsd |
Configuration XML 스키마 |
Build & Test
dotnet build
dotnet test
Pack (수동):
dotnet pack --configuration Release --output ./nupkg
Pack (스크립트):
./pack.sh # Directory.Build.props 버전 사용
./pack.sh 1.0.1 # 버전 지정
pack.sh는 빌드, 테스트, 패키징, 11개 패키지 검증을 자동 수행한다.
CI/CD
GitHub Actions 기반 CI/CD 파이프라인:
| Workflow | Trigger | 역할 |
|---|---|---|
ci.yml |
push (main, develop), PR | 빌드, 테스트, 코드 커버리지, 패키지 생성 검증 |
publish.yml |
v* 태그 push |
빌드, 테스트, NuGet.org 배포, GitHub Release 생성 |
benchmark.yml |
push (main), PR | BenchmarkDotNet 성능 벤치마크 실행 및 회귀 감지 |
e2e-testcontainers.yml |
push (main), PR | Testcontainers 기반 PostgreSQL/MySQL 멀티버전 E2E 테스트 |
docs.yml |
push (main, docs/**) | DocFX 문서 빌드 및 GitHub Pages 배포 |
NuGet 배포는 Trusted Publishing (OIDC) 방식을 사용한다. API 키를 저장하지 않고, GitHub Actions가 발급하는 단기 OIDC 토큰으로 NuGet.org 임시 API 키를 획득하여 배포한다.
릴리스 방법:
git tag v1.0.0
git push origin v1.0.0
태그 push 시 publish.yml이 자동 실행되어 11개 패키지를 NuGet.org에 배포하고 GitHub Release를 자동 생성한다.
Project Structure
NuVatis.sln
Directory.Build.props # 공통 NuGet 메타데이터 + 버전
pack.sh # NuGet 패키징 스크립트
schemas/
nuvatis-mapper.xsd # Mapper XML 스키마
nuvatis-config.xsd # Config XML 스키마
src/
NuVatis.Core/ # 핵심 런타임
NuVatis.Generators/ # Roslyn Source Generator
NuVatis.PostgreSql/ # PostgreSQL Provider
NuVatis.MySql/ # MySQL/MariaDB Provider
NuVatis.SqlServer/ # SQL Server Provider
NuVatis.Sqlite/ # SQLite Provider
NuVatis.Extensions.DependencyInjection/ # DI + Health Check
NuVatis.Extensions.OpenTelemetry/ # OpenTelemetry 분산 추적
NuVatis.Extensions.EntityFrameworkCore/ # EF Core 통합
NuVatis.Extensions.Aspire/ # .NET Aspire 통합
NuVatis.Testing/ # 테스트 유틸리티
tests/
NuVatis.Tests/ # 단위/통합/E2E 테스트 (311개)
NuVatis.Generators.Tests/ # Source Generator 테스트 (68개)
benchmarks/
NuVatis.Benchmarks/ # 성능 벤치마크
samples/
NuVatis.Sample/ # 사용 예제
Requirements
- .NET 7.0+ (.NET 7 / .NET 8 멀티 타겟)
- C# 11+
License
MIT License. Copyright (c) 2026 Jinho Choi.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net7.0
- Microsoft.EntityFrameworkCore.Relational (>= 7.0.20)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 7.0.0)
- NuVatis.Core (>= 1.0.4)
- NuVatis.Extensions.DependencyInjection (>= 1.0.4)
-
net8.0
- Microsoft.EntityFrameworkCore.Relational (>= 8.0.24)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- NuVatis.Core (>= 1.0.4)
- NuVatis.Extensions.DependencyInjection (>= 1.0.4)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.