.NET에서 Dapper로 데이터베이스 연결을 어떻게 처리합니까?
Dapper를 가지고 놀았지만 데이터베이스 연결을 처리하는 가장 좋은 방법을 모르겠습니다.
대부분의 예제는 예제 클래스 또는 각 메서드에서 생성되는 연결 개체를 보여줍니다. 그러나 web.config에서 가져 오는 경우에도 모든 clss에서 연결 문자열을 참조하는 것이 잘못되었습니다.
내 경험은 DbDataContext
또는 DbContext
Linq to SQL 또는 Entity Framework를 사용하여 왔 으므로 이것은 나에게 새로운 것입니다.
Dapper를 데이터 액세스 전략으로 사용할 때 웹 앱을 어떻게 구성합니까?
구성에서 연결 문자열을 검색하는 속성을 사용하여 확장 메서드를 만들었습니다. 이렇게하면 호출자가 연결이 열려 있는지 닫혀 있는지 여부 등 연결에 대해 알 필요가 없습니다.이 방법은 Dapper 기능 중 일부를 숨기고 있기 때문에 약간 제한되지만 우리의 매우 간단한 앱에서는 잘 작동합니다. , 그리고 Dapper에서 더 많은 기능이 필요한 경우이를 노출하는 새로운 확장 메서드를 항상 추가 할 수 있습니다.
internal static string ConnectionString = new Configuration().ConnectionString;
internal static IEnumerable<T> Query<T>(string sql, object param = null)
{
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
conn.Open();
return conn.Query<T>(sql, param);
}
}
internal static int Execute(string sql, object param = null)
{
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
conn.Open();
return conn.Execute(sql, param);
}
}
약 4 년 전에 질문을 받았지만 어쨌든 대답은 여기 누군가에게 유용 할 것입니다.
나는 모든 프로젝트에서 이렇게한다. 먼저 다음과 같은 몇 가지 도우미 메서드를 포함하는 기본 클래스를 만듭니다.
public class BaseRepository
{
protected T QueryFirstOrDefault<T>(string sql, object parameters = null)
{
using (var connection = CreateConnection())
{
return connection.QueryFirstOrDefault<T>(sql, parameters);
}
}
protected List<T> Query<T>(string sql, object parameters = null)
{
using (var connection = CreateConnection())
{
return connection.Query<T>(sql, parameters).ToList();
}
}
protected int Execute(string sql, object parameters = null)
{
using (var connection = CreateConnection())
{
return connection.Execute(sql, parameters);
}
}
// Other Helpers...
private IDbConnection CreateConnection()
{
var connection = new SqlConnection(...);
// Properly initialize your connection here.
return connection;
}
}
그리고 이러한 기본 클래스가 있으면 상용구 코드 없이도 실제 리포지토리를 쉽게 만들 수 있습니다.
public class AccountsRepository : BaseRepository
{
public Account GetById(int id)
{
return QueryFirstOrDefault<Account>("SELECT * FROM Accounts WHERE Id = @Id", new { id });
}
public List<Account> GetAll()
{
return Query<Account>("SELECT * FROM Accounts ORDER BY Name");
}
// Other methods...
}
따라서 Dapper, SqlConnection-s 및 기타 데이터베이스 액세스 항목과 관련된 모든 코드가 한곳 (BaseRepository)에 있습니다. 모든 실제 리포지토리는 깨끗하고 간단한 단선 방법입니다.
누군가를 도울 수 있기를 바랍니다.
Microsoft.AspNetCore.All : v2.0.3 | Dapper : v1.50.2
모범 사례를 올바르게 사용하고 있는지 확실하지 않지만 여러 연결 문자열 을 처리하기 위해 이렇게하고 있습니다.
연결 문자열이 하나만 있으면 쉽습니다.
Startup.cs
using System.Data;
using System.Data.SqlClient;
namespace DL.SO.Project.Web.UI
{
public class Startup
{
public IConfiguration Configuration { get; private set; }
// ......
public void ConfigureServices(IServiceCollection services)
{
// Read the connection string from appsettings.
string dbConnectionString = this.Configuration.GetConnectionString("dbConnection1");
// Inject IDbConnection, with implementation from SqlConnection class.
services.AddTransient<IDbConnection>((sp) => new SqlConnection(dbConnectionString));
// Register your regular repositories
services.AddScoped<IDiameterRepository, DiameterRepository>();
// ......
}
}
}
DiameterRepository.cs
using Dapper;
using System.Data;
namespace DL.SO.Project.Persistence.Dapper.Repositories
{
public class DiameterRepository : IDiameterRepository
{
private readonly IDbConnection _dbConnection;
public DiameterRepository(IDbConnection dbConnection)
{
_dbConnection = dbConnection;
}
public IEnumerable<Diameter> GetAll()
{
const string sql = @"SELECT * FROM TABLE";
// No need to use using statement. Dapper will automatically
// open, close and dispose the connection for you.
return _dbConnection.Query<Diameter>(sql);
}
// ......
}
}
연결 문자열이 두 개 이상인 경우 문제
를 Dapper
활용 하므로 IDbConnection
서로 다른 데이터베이스 연결을 구별하는 방법을 생각해야합니다.
IDbConnection
다른 데이터베이스 연결에 해당하는 에서 '상속'된 여러 인터페이스를 만들고 SqlConnection
에 다른 데이터베이스 연결 문자열을 삽입 하려고 했습니다 Startup
.
실패 때문 SqlConnection
로부터 상속 DbConnection
및 DbConnection
inplements뿐만 IDbConnection
아니라 Component
클래스입니다. 따라서 사용자 정의 인터페이스는 표현만을 사용할 수 없습니다 SqlConnection
.
또한 DbConnection
다른 연결 문자열 을 사용하는 자체 클래스 를 만들려고했습니다 . DbConnection
클래스의 모든 메서드를 구현해야하기 때문에 너무 복잡합니다 . 에서 도움을 잃었습니다 SqlConnection
.
내가하는 일
- 동안
Startup
, 나는 사전에 모든 연결 문자열 값을로드. 또한enum
매직 문자열을 피하기 위해 모든 데이터베이스 연결 이름에 대해을 만들었습니다 . - 저는 사전을 Singleton으로 삽입했습니다.
- 주입하는 대신 모든 리포지토리에 대해 Transient를
IDbConnection
만들고IDbConnectionFactory
주입했습니다. 이제 모든 리포지토리IDbConnectionFactory
가IDbConnection
. - 언제 올바른 연결을 선택해야합니까? 모든 저장소의 생성자에서! 깔끔하게 만들기 위해 저장소 기본 클래스를 만들고 저장소가 기본 클래스에서 상속 받도록했습니다. 올바른 연결 문자열 선택은 기본 클래스에서 발생할 수 있습니다.
DatabaseConnectionName.cs
namespace DL.SO.Project.Domain.Repositories
{
public enum DatabaseConnectionName
{
Connection1,
Connection2
}
}
IDbConnectionFactory.cs
using System.Data;
namespace DL.SO.Project.Domain.Repositories
{
public interface IDbConnectionFactory
{
IDbConnection CreateDbConnection(DatabaseConnectionName connectionName);
}
}
DapperDbConenctionFactory-나만의 팩토리 구현
namespace DL.SO.Project.Persistence.Dapper
{
public class DapperDbConnectionFactory : IDbConnectionFactory
{
private readonly IDictionary<DatabaseConnectionName, string> _connectionDict;
public DapperDbConnectionFactory(IDictionary<DatabaseConnectionName, string> connectionDict)
{
_connectionDict = connectionDict;
}
public IDbConnection CreateDbConnection(DatabaseConnectionName connectionName)
{
string connectionString = null;
if (_connectDict.TryGetValue(connectionName, out connectionString))
{
return new SqlConnection(connectionString);
}
throw new ArgumentNullException();
}
}
}
Startup.cs
namespace DL.SO.Project.Web.UI
{
public class Startup
{
// ......
public void ConfigureServices(IServiceCollection services)
{
var connectionDict = new Dictionary<DatabaseConnectionName, string>
{
{ DatabaseConnectionName.Connection1, this.Configuration.GetConnectionString("dbConnection1") },
{ DatabaseConnectionName.Connection2, this.Configuration.GetConnectionString("dbConnection2") }
};
// Inject this dict
services.AddSingleton<IDictionary<DatabaseConnectionName, string>>(connectionDict);
// Inject the factory
services.AddTransient<IDbConnectionFactory, DapperDbConnectionFactory>();
// Register your regular repositories
services.AddScoped<IDiameterRepository, DiameterRepository>();
// ......
}
}
}
DiameterRepository.cs
using Dapper;
using System.Data;
namespace DL.SO.Project.Persistence.Dapper.Repositories
{
// Move the responsibility of picking the right connection string
// into an abstract base class so that I don't have to duplicate
// the right connection selection code in each repository.
public class DiameterRepository : DbConnection1RepositoryBase, IDiameterRepository
{
public DiameterRepository(IDbConnectionFactory dbConnectionFactory)
: base(dbConnectionFactory) { }
public IEnumerable<Diameter> GetAll()
{
const string sql = @"SELECT * FROM TABLE";
// No need to use using statement. Dapper will automatically
// open, close and dispose the connection for you.
return base.DbConnection.Query<Diameter>(sql);
}
// ......
}
}
DbConnection1RepositoryBase.cs
using System.Data;
using DL.SO.Project.Domain.Repositories;
namespace DL.SO.Project.Persistence.Dapper
{
public abstract class DbConnection1RepositoryBase
{
public IDbConnection DbConnection { get; private set; }
public DbConnection1RepositoryBase(IDbConnectionFactory dbConnectionFactory)
{
// Now it's the time to pick the right connection string!
// Enum is used. No magic string!
this.DbConnection = dbConnectionFactory.CreateDbConnection(DatabaseConnectionName.Connection1);
}
}
}
그런 다음 다른 연결과 통신해야하는 다른 저장소에 대해 다른 저장소 기본 클래스를 만들 수 있습니다.
using System.Data;
using DL.SO.Project.Domain.Repositories;
namespace DL.SO.Project.Persistence.Dapper
{
public abstract class DbConnection2RepositoryBase
{
public IDbConnection DbConnection { get; private set; }
public DbConnection2RepositoryBase(IDbConnectionFactory dbConnectionFactory)
{
this.DbConnection = dbConnectionFactory.CreateDbConnection(DatabaseConnectionName.Connection2);
}
}
}
using Dapper;
using System.Data;
namespace DL.SO.Project.Persistence.Dapper.Repositories
{
public class ParameterRepository : DbConnection2RepositoryBase, IParameterRepository
{
public ParameterRepository (IDbConnectionFactory dbConnectionFactory)
: base(dbConnectionFactory) { }
public IEnumerable<Parameter> GetAll()
{
const string sql = @"SELECT * FROM TABLE";
return base.DbConnection.Query<Parameter>(sql);
}
// ......
}
}
이 모든 도움을 바랍니다.
나는 이것을 이렇게한다 :
internal class Repository : IRepository {
private readonly Func<IDbConnection> _connectionFactory;
public Repository(Func<IDbConnection> connectionFactory)
{
_connectionFactory = connectionFactory;
}
public IWidget Get(string key) {
using(var conn = _connectionFactory())
{
return conn.Query<Widget>(
"select * from widgets with(nolock) where widgetkey=@WidgetKey", new { WidgetKey=key });
}
}
}
그런 다음 종속성 (예 : Global.asax.cs 또는 Startup.cs)을 연결할 때마다 다음과 같은 작업을 수행합니다.
var connectionFactory = new Func<IDbConnection>(() => {
var conn = new SqlConnection(
ConfigurationManager.ConnectionStrings["connectionString-name"];
conn.Open();
return conn;
});
모범 사례는 실제로드 된 용어입니다. 나는 DapperDbDataContext
와 같은 스타일 컨테이너를 좋아 합니다. , 트랜잭션 및 기타 도우미 를 연결할 수 있습니다 .CommandTimeout
예를 들면 :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using Dapper;
// to have a play, install Dapper.Rainbow from nuget
namespace TestDapper
{
class Program
{
// no decorations, base class, attributes, etc
class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime? LastPurchase { get; set; }
}
// container with all the tables
class MyDatabase : Database<MyDatabase>
{
public Table<Product> Products { get; set; }
}
static void Main(string[] args)
{
var cnn = new SqlConnection("Data Source=.;Initial Catalog=tempdb;Integrated Security=True");
cnn.Open();
var db = MyDatabase.Init(cnn, commandTimeout: 2);
try
{
db.Execute("waitfor delay '00:00:03'");
}
catch (Exception)
{
Console.WriteLine("yeah ... it timed out");
}
db.Execute("if object_id('Products') is not null drop table Products");
db.Execute(@"create table Products (
Id int identity(1,1) primary key,
Name varchar(20),
Description varchar(max),
LastPurchase datetime)");
int? productId = db.Products.Insert(new {Name="Hello", Description="Nothing" });
var product = db.Products.Get((int)productId);
product.Description = "untracked change";
// snapshotter tracks which fields change on the object
var s = Snapshotter.Start(product);
product.LastPurchase = DateTime.UtcNow;
product.Name += " World";
// run: update Products set LastPurchase = @utcNow, Name = @name where Id = @id
// note, this does not touch untracked columns
db.Products.Update(product.Id, s.Diff());
// reload
product = db.Products.Get(product.Id);
Console.WriteLine("id: {0} name: {1} desc: {2} last {3}", product.Id, product.Name, product.Description, product.LastPurchase);
// id: 1 name: Hello World desc: Nothing last 12/01/2012 5:49:34 AM
Console.WriteLine("deleted: {0}", db.Products.Delete(product.Id));
// deleted: True
Console.ReadKey();
}
}
}
이 시도:
public class ConnectionProvider
{
DbConnection conn;
string connectionString;
DbProviderFactory factory;
// Constructor that retrieves the connectionString from the config file
public ConnectionProvider()
{
this.connectionString = ConfigurationManager.ConnectionStrings[0].ConnectionString.ToString();
factory = DbProviderFactories.GetFactory(ConfigurationManager.ConnectionStrings[0].ProviderName.ToString());
}
// Constructor that accepts the connectionString and Database ProviderName i.e SQL or Oracle
public ConnectionProvider(string connectionString, string connectionProviderName)
{
this.connectionString = connectionString;
factory = DbProviderFactories.GetFactory(connectionProviderName);
}
// Only inherited classes can call this.
public DbConnection GetOpenConnection()
{
conn = factory.CreateConnection();
conn.ConnectionString = this.connectionString;
conn.Open();
return conn;
}
}
모두가 너무 일찍 연결을 시작하는 것 같습니까? 나는이 같은 질문이 있었고 여기 소스를 파고 들었다-https: //github.com/StackExchange/dapper-dot-net/blob/master/Dapper/SqlMapper.cs
데이터베이스와의 모든 상호 작용은 연결이 닫혀 있는지 확인하기 위해 연결을 확인하고 필요에 따라 엽니 다. 이로 인해 conn.open ()없이 위와 같은 구문을 사용하기 만하면됩니다. 이렇게하면 가능한 한 상호 작용에 가깝게 연결이 열립니다. 눈치 채면 즉시 연결이 종료됩니다. 이것은 또한 폐기 중에 자동으로 닫히는 것보다 빠릅니다.
위의 저장소에서 이에 대한 많은 예 중 하나 :
private static int ExecuteCommand(IDbConnection cnn, ref CommandDefinition command, Action<IDbCommand, object> paramReader)
{
IDbCommand cmd = null;
bool wasClosed = cnn.State == ConnectionState.Closed;
try
{
cmd = command.SetupCommand(cnn, paramReader);
if (wasClosed) cnn.Open();
int result = cmd.ExecuteNonQuery();
command.OnCompleted();
return result;
}
finally
{
if (wasClosed) cnn.Close();
cmd?.Dispose();
}
}
다음은 DapperWrapper라는 Dapper 용 Wrapper를 사용하는 방법에 대한 간단한 예입니다. 이를 통해 Dapper 및 Simple Crud 메서드를 모두 래핑하여 연결을 관리하고 보안, 로깅 등을 제공 할 수 있습니다.
public class DapperWrapper : IDapperWrapper
{
public IEnumerable<T> Query<T>(string query, object param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
{
using (var conn = Db.NewConnection())
{
var results = conn.Query<T>(query, param, transaction, buffered, commandTimeout, commandType);
// Do whatever you want with the results here
// Such as Security, Logging, Etc.
return results;
}
}
}
안녕하세요 @donaldhughes 저도 처음 사용하고 있습니다. 1-연결 문자열을 얻기위한 클래스 만들기 2-사용에서 연결 문자열 클래스를 호출합니다.
보기:
DapperConnection.cs
public class DapperConnection
{
public IDbConnection DapperCon {
get
{
return new SqlConnection(ConfigurationManager.ConnectionStrings["Default"].ToString());
}
}
}
DapperRepository.cs
public class DapperRepository : DapperConnection
{
public IEnumerable<TBMobileDetails> ListAllMobile()
{
using (IDbConnection con = DapperCon )
{
con.Open();
string query = "select * from Table";
return con.Query<TableEntity>(query);
}
}
}
그리고 잘 작동합니다.
도우미 클래스와의 연결을 래핑합니다.
public class ConnectionFactory
{
private readonly string _connectionName;
public ConnectionFactory(string connectionName)
{
_connectionName = connectionName;
}
public IDbConnection NewConnection() => new SqlConnection(_connectionName);
#region Connection Scopes
public TResult Scope<TResult>(Func<IDbConnection, TResult> func)
{
using (var connection = NewConnection())
{
connection.Open();
return func(connection);
}
}
public async Task<TResult> ScopeAsync<TResult>(Func<IDbConnection, Task<TResult>> funcAsync)
{
using (var connection = NewConnection())
{
connection.Open();
return await funcAsync(connection);
}
}
public void Scope(Action<IDbConnection> func)
{
using (var connection = NewConnection())
{
connection.Open();
func(connection);
}
}
public async Task ScopeAsync<TResult>(Func<IDbConnection, Task> funcAsync)
{
using (var connection = NewConnection())
{
connection.Open();
await funcAsync(connection);
}
}
#endregion Connection Scopes
}
사용 예 :
public class PostsService
{
protected IConnectionFactory Connection;
// Initialization here ..
public async Task TestPosts_Async()
{
// Normal way..
var posts = Connection.Scope(cnn =>
{
var state = PostState.Active;
return cnn.Query<Post>("SELECT * FROM [Posts] WHERE [State] = @state;", new { state });
});
// Async way..
posts = await Connection.ScopeAsync(cnn =>
{
var state = PostState.Active;
return cnn.QueryAsync<Post>("SELECT * FROM [Posts] WHERE [State] = @state;", new { state });
});
}
}
따라서 매번 명시 적으로 연결을 열 필요가 없습니다. 또한 향후 리팩토링의 편의를 위해 다음과 같은 방식으로 사용할 수 있습니다.
var posts = Connection.Scope(cnn =>
{
var state = PostState.Active;
return cnn.Query<Post>($"SELECT * FROM [{TableName<Post>()}] WHERE [{nameof(Post.State)}] = @{nameof(state)};", new { state });
});
이 답변TableName<T>()
에서 무엇을 찾을 수 있습니다 .
참고 URL : https://stackoverflow.com/questions/9218847/how-do-i-handle-database-connections-with-dapper-in-net
'IT Share you' 카테고리의 다른 글
우리가 사용하면 안돼 (0) | 2020.11.08 |
---|---|
R 프로그래밍에서 S4 방법을 사용하는 것이 언제 효과가 있습니까? (0) | 2020.11.08 |
Go HTTP 핸들러에서 ResponseWriter는 값이지만 Request는 포인터 인 이유는 무엇입니까? (0) | 2020.11.08 |
PHPUnit에서 시간을 "모의"할 수 있습니까? (0) | 2020.11.08 |
단항 연산자를 오버로드하는 타당한 이유는 무엇입니까? (0) | 2020.11.08 |