如何在.NET中处理与Dapper的数据库连接?

我一直在玩Dapper,但是我不确定处理数据库连接的最佳方法。

大多数示例显示了在示例类中甚至在每个方法中创build的连接对象。 但是我觉得在每个clss中引用连接string都是错误的,即使它是从we​​b.config中提取的。

我的经验是使用DbDataContextDbContext与Linq到SQL或entity framework,所以这对我来说是新的。

在使用Dapper作为我的数据访问策略时,如何构build我的Web应用程序?

我使用从configuration中检索连接string的属性创build了扩展方法。 这使得调用者不必知道连接的任何信息,无论是打开还是closures等。这个方法确实限制了你一些,因为你隐藏了一些Dapper的function,但是在我们相当简单的应用程序中,它对我们来说工作得很好,如果我们需要Dapper的更多function,我们总是可以添加一个新的扩展方法来公开它。

 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年前就问过这个问题,但无论如何,也许答案对这里的某个人有用:

我在所有的项目中都这样做。 首先,我创build一个基类,其中包含一些像这样的辅助方法:

 public class BaseRepository { protected T QueryFirstOrDefault<T>(string sql, object parameters = null) { using (var connection = CreateConnection()) { connection.Open(); return connection.QueryFirstOrDefault<T>(sql, parameters); } } protected List<T> Query<T>(string sql, object parameters = null) { using (var connection = CreateConnection()) { connection.Open(); return connection.Query<T>(sql, parameters).ToList(); } } protected int Execute(string sql, object parameters = null) { using (var connection = CreateConnection()) { connection.Open(); return connection.Execute(sql, parameters); } } // Other Helpers... private IDbConnection CreateConnection() { var connection = new SqlConnection(...); // Properly initialize your connection here. return connection; } } 

有了这样一个基类,我可以很容易地创build真正的仓库没有任何样板代码:

 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)。 所有真正的软件库都是干净而简单的1行方法。

我希望这会帮助别人。

最佳做法是一个真正的加载术语。 我喜欢像DbDataContext风格的容器推广。 它允许你连接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 ie 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; } } 

我这样做:

 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; }); 

每个人似乎都打得太早了? 我有这个相同的问题,并通过源在这里挖 – https://github.com/StackExchange/dapper-dot-net/blob/master/Dapper/SqlMapper.cs

你会发现每个与数据库的交互都会检查连接是否被closures,并根据需要打开连接。 由于这个原因,我们只是简单地使用上面没有conn.open()的语句。 通过这种方式,连接尽可能接近交互。 如果你注意到,它也立即closures连接。 这也比在处理过程中自动closures更快。

从上面的回购这个很多例子之一:

  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(); } } 

下面是我们如何使用Dapper Wrapper的一个小例子,叫做DapperWrapper。 这使得我们可以包装所有的Dapper和Simple Crud方法来pipe理连接,提供安全性,日志logging等。

  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; } } } 

Microsoft.AspNetCore.All :v2.0.3 | Dapper :v1.50.2

我不知道我是否正确使用最佳实践,但是我这样做是为了处理多个连接string。

如果只有一个连接string很容易

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); } // ...... } } 

问题,如果你有超过1个连接string

由于Dapper使用IDbConnection ,所以您需要考虑区分不同数据库连接的方法。

我尝试创build多个接口,从IDbConnection “inheritance”,对应于不同的数据库连接,并在Startup注入具有不同数据库连接string的SqlConnection

这是因为SqlConnectionDbConnectioninheritance,而DbConnection不仅包含IDbConnection ,还包含Component类。 所以你的自定义接口将不能使用SqlConnection实现。

我也尝试创build我自己的DbConnection类,它采用不同的连接string。 这太复杂了,因为你必须实现DbConnection类的所有方法。 你失去了SqlConnection的帮助。

我最终做了什么

  1. Startup ,我将所有连接string值加载到字典中。 我还创build了所有数据库连接名称的enum ,以避免魔术string。
  2. 我把字典注入Singleton。
  3. 我没有注入IDbConnection ,而是创build了IDbConnectionFactory并将其注入为所有存储库的Transient。 现在所有的存储库都采用IDbConnectionFactory而不是IDbConnection
  4. 何时select正确的连接? 在所有的仓库的构造函数! 为了使事情变得干净,我创build了存储库基类,并让存储库从基类inheritance。 正确的连接stringselect可以在基类中发生。

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); } } } 

然后,对于需要与其他连接进行通信的其他存储库,可以为它们创build不同的存储库基类。

 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); } // ...... } } 

希望所有这些帮助。

您好@donaldhughes我也是新的,我用它来做到这一点:1 – 创build一个类来获得我的连接string2 – 在一个使用调用连接string类

看:

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); } } } 

它工作正常。