Getting Started - lobodava/artisan-orm GitHub Wiki

To start using Artisan.Orm:

  1. Install the NuGet package, or add the Artisan.Orm project to your solution.

  2. 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 in Web.config or App.config and read it through ConfigurationManager.

    See About Connection Strings for environment-specific overrides, user secrets, and connection pooling notes.

  3. 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, RepositoryBase creates a SqlConnection and leaves it closed. The connection is opened on demand by Read* / Execute* methods and closed back when they finish.

  4. 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:

  5. Repository lifetime. RepositoryBase implements IDisposable and IAsyncDisposable. A repository instance owns one SqlConnection and a mutable Transaction field, 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 Scoped and 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 using statement:

    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);
            }
        }
    }
  6. Besides the repository class, you will typically also need:

⚠️ **GitHub.com Fallback** ⚠️