Wie gehe ich mit Datenbankverbindungen mit Dapper in .NET um?


85

Ich habe mit Dapper gespielt, bin mir aber nicht sicher, wie ich die Datenbankverbindung am besten handhaben soll.

Die meisten Beispiele zeigen das Verbindungsobjekt, das in der Beispielklasse oder sogar in jeder Methode erstellt wird. Es fühlt sich für mich jedoch falsch an, in jedem clss auf eine Verbindungszeichenfolge zu verweisen, selbst wenn diese aus der web.config stammt.

Meine Erfahrung war mit der Verwendung eines DbDataContextoder DbContextmit Linq to SQL oder Entity Framework, daher ist dies für mich neu.

Wie strukturiere ich meine Web-Apps, wenn ich Dapper als Datenzugriffsstrategie verwende?


Zu spät aber; Ich habe es so implementiert: stackoverflow.com/a/45029588/5779732
Amit Joshi

using-dapper-asynchron-in-asp-net-core-2 - exceptionnotfound.net/…
Himalaya Garg

Antworten:


50

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

Ich bin nicht sicher, ob ich die Best Practices richtig verwende oder nicht, aber ich mache es auf diese Weise, um mehrere Verbindungszeichenfolgen zu verarbeiten .

Es ist einfach, wenn Sie nur 1 Verbindungszeichenfolge haben

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

        // ......
    }
}

Probleme, wenn Sie mehr als eine Verbindungszeichenfolge haben

Da Dapperverwendet wird IDbConnection, müssen Sie sich eine Möglichkeit überlegen , verschiedene Datenbankverbindungen zu unterscheiden.

Ich habe versucht, mehrere Schnittstellen zu erstellen, die von IDbConnectionverschiedenen Datenbankverbindungen "geerbt" wurden , und SqlConnectionverschiedene Datenbankverbindungszeichenfolgen einzuschalten Startup.

Das ist fehlgeschlagen, weil SqlConnectionerbt von DbConnectionund DbConnectionimplementiert nicht nur, IDbConnectionsondern auch ComponentKlasse. Ihre benutzerdefinierten Schnittstellen können also nicht nur die SqlConnectionImplenentation verwenden.

Ich habe auch versucht, eine eigene DbConnectionKlasse zu erstellen , die eine andere Verbindungszeichenfolge verwendet. Das ist zu kompliziert, weil Sie alle Methoden aus der DbConnectionKlasse implementieren müssen . Du hast die Hilfe von verloren SqlConnection.

Was ich am Ende mache

  1. Währenddessen habe Startupich alle Verbindungszeichenfolgenwerte in ein Wörterbuch geladen. Ich habe auch einen Namen enumfür alle Datenbankverbindungen erstellt, um magische Zeichenfolgen zu vermeiden.
  2. Ich habe das Wörterbuch als Singleton injiziert.
  3. Anstatt zu injizieren IDbConnection, habe ich IDbConnectionFactorydas als Transient für alle Repositorys erstellt und injiziert. Jetzt nehmen alle Repositories IDbConnectionFactorystatt IDbConnection.
  4. Wann wählen Sie die richtige Verbindung? Im Konstruktor aller Repositories! Um die Dinge sauber zu machen, habe ich Repository-Basisklassen erstellt und die Repositorys von den Basisklassen erben lassen. Die richtige Auswahl der Verbindungszeichenfolge kann in den Basisklassen erfolgen.

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 - meine eigene Factory-Implementierung

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

Für andere Repositorys, die mit den anderen Verbindungen kommunizieren müssen, können Sie dann eine andere Repository-Basisklasse für diese erstellen.

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

        // ......
    }
}

Hoffe all diese Hilfe.


Genau das, wonach ich suche. Ich hatte das gleiche Problem und löste es auf die gleiche Weise. Ich weiß immer noch nicht, ob dies eine gute Praxis ist, aber meiner Meinung nach denke ich, dass es so ist.
Ewerton

1
Wäre es besser, IDbConnection für den IServiceProvider-Bereich zu registrieren? Man kann einen Dienst erstellen und sich als Singleton-Scope-Factory mit verschiedenen Verbindungen und mit var scope = factory.CreateNonDefaultScope () registrieren. Mit var connection = scope.ServiceProvider.GetRequiredService <IDbConnection> () erhalten Sie Ihre nicht standardmäßige Verbindung. Weniger Vererbung hilft auch bei der Erweiterbarkeit ...
auch

Das suche ich. Ehrfürchtiger Job @David. Danke
Shashwat Prakash

27

Ich habe Erweiterungsmethoden mit einer Eigenschaft erstellt, die die Verbindungszeichenfolge aus der Konfiguration abruft. Auf diese Weise müssen die Anrufer nichts über die Verbindung wissen, ob sie offen oder geschlossen ist usw. Diese Methode schränkt Sie ein wenig ein, da Sie einige der Dapper-Funktionen verbergen, aber in unserer recht einfachen App hat es für uns gut funktioniert Wenn wir mehr Funktionen von Dapper benötigen, können wir jederzeit eine neue Erweiterungsmethode hinzufügen, die diese verfügbar macht.

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

1
Eine Frage hier. Da conn.Query IEnumerable <T> zurückgibt, ist es sicher, das Verbindungsobjekt sofort zu entsorgen? Benötigt der IEnumerable nicht die Verbindung, um die Elemente beim Lesen zu materialisieren? Sollten wir eine ToList () ausführen?
Adrian Nasui

Ich müsste mich wieder an Dapper wenden, um dies zu überprüfen, aber ich bin mir ziemlich sicher, dass ich dieses Muster so übernommen habe, wie es aus dem funktionierenden Produktionscode stammt. Es sollte in Ordnung sein - aber natürlich sollten Sie jeden Code im Internet testen.
Shawn Hubbard

2
Wenn Sie eine adrette Abfrageerweiterungsmethode verwenden, müssen Sie die Verbindung nicht explizit öffnen, wie dies in der Methode selbst erfolgt.
h-rai

4
Das Problem mit dem obigen Code ist, dass, wenn Sie gepuffert: true an die Query-Methode übergeben, die Verbindung getrennt wird, bevor die Daten zurückgegeben werden. Intern verwandelt Dapper die Aufzählung vor der Rückkehr in eine Liste.
Brian Vallelunga

@ BrianVallelunga wäre das nicht buffered: false?
Jodrell

25

Es wurde vor ungefähr 4 Jahren gefragt ... aber vielleicht ist die Antwort für jemanden hier nützlich:

Ich mache es in allen Projekten so. Zuerst erstelle ich eine Basisklasse, die einige Hilfsmethoden wie diese enthält:

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

Und mit einer solchen Basisklasse kann ich problemlos echte Repositorys ohne Boilerplate-Code erstellen:

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

Der gesamte Code für Dapper, SqlConnection-s und andere Datenbankzugriffsmaterialien befindet sich also an einem Ort (BaseRepository). Alle realen Repositorys sind saubere und einfache 1-Zeilen-Methoden.

Ich hoffe es wird jemandem helfen.


2
BaseRepositoryist eine unnötige Vererbung, da keine öffentlichen oder abstrakten Methoden oder Eigenschaften bereitgestellt werden. Dies könnte stattdessen eine DBHelperKlasse sein.
Josh Noe

Vielleicht ist es besser, CreateConnectionin die eigene Klasse zu wechseln ?
Hellboy

Vielleicht ... Aber ich persönlich mag es, alles einfach zu halten. Wenn Sie in CreateConnection (...) viel Logik haben, ist dies möglicherweise eine gute Idee. In meinen Projekten ist diese Methode so einfach wie "Neue Verbindung zurückgeben (connectionString)", sodass sie ohne separate CreateConnection (...) -Methode inline verwendet werden kann.
Pavel Melnikov

1
Wie Nick-s hervorhob, müssen Sie in den neuesten Versionen von Dapper die Datenbankverbindung nicht manuell öffnen. Dapper öffnet es automatisch für Sie. hat den Beitrag aktualisiert.
Pavel Melnikov

injiziere es imo. services.AddScoped<IDbConnection>(p => new SqlConnection(connString)dann fragen Sie einfach danach, wo es nötig ist
Sinaesthetic

8

So mach ich es:

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

Wo immer ich dann meine Abhängigkeiten verkabele (z. B. Global.asax.cs oder Startup.cs), mache ich so etwas wie:

var connectionFactory = new Func<IDbConnection>(() => {
    var conn = new SqlConnection(
        ConfigurationManager.ConnectionStrings["connectionString-name"];
    conn.Open();
    return conn;
});

Eine Frage hier. Ist es sicher, die Verbindung sofort zu entsorgen, da conn.Query Ienumerable <T> zurückgibt? Benötigt der IEnumerable nicht die Verbindung, um die Elemente beim Lesen zu materialisieren?
Adrian Nasui

1
@AdrianNasui: Derzeit führt Dapper standardmäßig SQL aus und puffert den gesamten Reader bei der Rückgabe, sodass der IEnumerable<T>bereits materialisiert ist. Wenn Sie bestanden haben buffered: false, müssen Sie die Ausgabe verbrauchen, bevor Sie den usingBlock verlassen.
Jacob Krall

7

Best Practice ist ein wirklich geladener Begriff. Ich mag einen DbDataContextStyle-Container wie Dapper.Rainbow . Sie können die CommandTimeoutTransaktions- und andere Helfer koppeln .

Zum Beispiel:

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

15
Fragt das OP nicht mehr nach dem Teil SqlConnection ([[CONN STRING HERE]])? Er sagt: "Aber es fühlt sich für mich falsch an, in jeder Klasse (auch in jeder Methode) auf eine Verbindungszeichenfolge zu verweisen." Ich denke, er fragt sich, ob wir Dapper-Benutzer ein Muster (in gewisser Weise) generiert haben, um die Seite der Verbindungserstellung der Dinge zu verpacken TROCKEN / diese Logik verstecken. (Neben dem OP, wenn Sie Dapper.Rainbow verwenden können, tun Sie dies ... es ist wirklich schön!)
ckittel

4

Versuche dies:

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

    }

6
Wie gehen Sie mit dem Schließen / Entsorgen der Verbindung in Ihrer Lösung um?
Jpshook

@JPShook - Ich glaube, er benutzt mit. (ref stackoverflow.com/a/4717859/2133703 )
MacGyver

4

Jeder scheint seine Verbindungen viel zu früh zu öffnen? Ich hatte dieselbe Frage und nachdem ich die Quelle hier durchgesehen hatte - https://github.com/StackExchange/dapper-dot-net/blob/master/Dapper/SqlMapper.cs

Sie werden feststellen, dass jede Interaktion mit der Datenbank die Verbindung überprüft, um festzustellen, ob sie geschlossen ist, und sie bei Bedarf öffnet. Aus diesem Grund verwenden wir einfach Anweisungen wie oben ohne conn.open (). Auf diese Weise wird die Verbindung so nah wie möglich an der Interaktion geöffnet. Wenn Sie bemerken, wird auch die Verbindung sofort geschlossen. Dies ist auch schneller als das automatische Schließen während der Entsorgung.

Eines der vielen Beispiele aus dem obigen Repo:

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

Unten finden Sie ein kleines Beispiel dafür, wie wir einen Wrapper für Dapper verwenden, den DapperWrapper. Auf diese Weise können wir alle Dapper- und Simple Crud-Methoden umschließen, um Verbindungen zu verwalten, Sicherheit zu bieten, Protokolle zu erstellen usw.

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

1
Dies ist sehr nützlich, wenn Sie wissen, dass Dapper die Verbindung offen lässt, wenn sie zum Zeitpunkt des Abrufs bereits geöffnet ist. Ich öffne jetzt die Datenbankverbindung vor, bevor ich sie mit Dapper übergebe / verwende, und ich habe einen 6-fachen Leistungsgewinn erhalten - danke!
Chris Smith

2

Ich wickle die Verbindung mit der Hilfsklasse ein:

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
}

Anwendungsbeispiele:

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

Ich muss die Verbindung also nicht jedes Mal explizit öffnen. Darüber hinaus können Sie es auf diese Weise verwenden, um das zukünftige Refactoring zu vereinfachen:

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

Was ist, TableName<T>()kann in dieser Antwort gefunden werden .


0

Hallo @donaldhughes, ich bin auch neu darin und mache Folgendes: 1 - Erstellen Sie eine Klasse, um meine Verbindungszeichenfolge abzurufen. 2 - Rufen Sie die Verbindungszeichenfolgenklasse in einem Using auf

Aussehen:

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

Und es funktioniert gut.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.