Getting Started - lobodava/artisan-orm GitHub Wiki
To start using Artisan.Orm:
-
Install the NuGet package, or add the Artisan.Orm project to your solution.
-
Add the connection string to your configuration. For modern .NET (.NET 6 / 8 / 10):
// appsettings.json { "ConnectionStrings": { "DatabaseConnection": "Data Source=.\\SQLEXPRESS;Initial Catalog=Artisan;Integrated Security=True;TrustServerCertificate=True;" } }
For .NET Framework, use the
<connectionStrings>section inWeb.configorApp.configand read it throughConfigurationManager.See About Connection Strings for environment-specific overrides, user secrets, and connection pooling notes.
-
Create a repository class deriving from
Artisan.Orm.RepositoryBase. The base class takes the connection string explicitly:using Artisan.Orm; public class UserRepository : RepositoryBase { public UserRepository(string connectionString) : base(connectionString) { } }
When the repository is instantiated,
RepositoryBasecreates aSqlConnectionand leaves it closed. The connection is opened on demand byRead*/Execute*methods and closed back when they finish. -
Add your data-access methods on top of the SqlCommand initialization helpers:
public User? GetUserById(int id) { return GetByCommand(cmd => // SqlConnection is still closed here { cmd.UseProcedure("dbo.GetUserById"); cmd.AddIntParam("@Id", id); // The cmd.ReadTo extension opens SqlConnection, // reads the data, closes SqlConnection, and returns the result. return cmd.ReadTo<User>(); }); } public void DeleteUser(int userId) { ExecuteCommand(cmd => // SqlConnection is closed here { cmd.UseProcedure("dbo.DeleteUser"); cmd.AddIntParam("@UserId", userId); } // After command configuration, ExecuteCommand opens SqlConnection, // executes ExecuteNonQuery, and closes SqlConnection again. ); }
Read more about extension methods provided by Artisan.Orm:
-
Repository lifetime.
RepositoryBaseimplementsIDisposableandIAsyncDisposable. A repository instance owns oneSqlConnectionand a mutableTransactionfield, so it is not safe to share across concurrent operations — treat one repository as one unit of work. Three idiomatic patterns:ASP.NET Core / DI containers — register as
Scopedand inject. The container disposes the repository at the end of the HTTP request automatically.// Program.cs var connStr = builder.Configuration.GetConnectionString("DatabaseConnection")!; builder.Services.AddScoped(_ => new UserRepository(connStr));
// Controller public class UsersController : ControllerBase { private readonly UserRepository _repo; public UsersController(UserRepository repo) => _repo = repo; [HttpGet("{id:int}")] public User? Get(int id) => _repo.GetUserById(id); }
See Using Artisan.Orm in ASP.NET Core for the complete picture: background services, sharing transactions across services in one request, why not
Singleton/Transient.Console apps, scripts, one-off code — use a
usingstatement:using var repo = new UserRepository(connStr); var user = repo.GetUserById(1);
Long-running workers without an ambient DI scope — pull a fresh repository per work item via
IServiceScopeFactory:public class ImportWorker : BackgroundService { private readonly IServiceScopeFactory _scopeFactory; public ImportWorker(IServiceScopeFactory scopeFactory) => _scopeFactory = scopeFactory; protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { using var scope = _scopeFactory.CreateScope(); var repo = scope.ServiceProvider.GetRequiredService<RecordRepository>(); await repo.ImportNextBatchAsync(stoppingToken); await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken); } } }
-
Besides the repository class, you will typically also need:
- Mappers (C#) for each type you read or save,
- User-defined table types (T-SQL) for types you save,
- Stored procedures (T-SQL) to read, save, and delete objects.