Laden...

Welche (neuen) Zugriffstechnologien für SQL-Server / Firebird und C#? QLink & Co. verwenden?

Erstellt von GigaMann vor 6 Jahren Letzter Beitrag vor 6 Jahren 3.504 Views
G
GigaMann Themenstarter:in
2 Beiträge seit 2018
vor 6 Jahren
Welche (neuen) Zugriffstechnologien für SQL-Server / Firebird und C#? QLink & Co. verwenden?

verwendetes Datenbanksystem: MS SQL / Firebird

Hallo zusammen,

nach langer Abwesenheit möchte ich nun wieder in C# einsteigen. Mit Visual Studio 2010 habe ich unter C# mit LinkQ auf die Datenbank zugegriffen. Unter Delphi hatte ich zur Laufzeit SQL-Abfragen dynamisch erzeugt.
Nun frage ich mich, welche Technologie man nun mir Visual Studio 2017 verwenden sollte. Gibt es da mittlerweile andere Konzepte oder ist LinkQ noch die erste Wahl?
Es geht um allgemeine Datenbankzugriffe auch von komplexen SQL's über mehrere Tabellen inkl. Where-Bedingungen etc.

Vielen Dank für ein Update, was man mittlerweile in C# verwenden sollte.

Viele Grüße

Axel

F
10.010 Beiträge seit 2004
vor 6 Jahren
  1. Visual Studio ist eine IDE, keine Programmiersprache.
  2. Es gibt kein LinkQ
  3. Wenn du LinQ meinst, das ist nur ein bisschen Syntactic Sugar um mit listen zu arbeiten.

Du siehst es ist schon ziemlich wichtig die richtigen Begriffe zu benutzen, denn sonst kommt man nicht zum ziel.

Und was man verwenden sollte artet regelmäßig in Glaubenskriege aus.
Es gibt welche die von diesen ganzen großen ORMappern nichts halten und Sachen wie Dapper einsetzen.
Dann gibt es ( immer noch ) die Leute die NHypernate einsetzen.

Ich persönlich habe die besten Erfahrungen mit Linq2DB in Verbindung mit FluentMigrator gemacht.
Wenn du aber z.b. an Crossplatform denkst ( .NET Core ) dann wäre eher EF Core die Sache.

Aber als erstes solltest du entscheiden welche DB du wirklich verwenden möchtest,

286 Beiträge seit 2011
vor 6 Jahren

Und was man verwenden sollte artet regelmäßig in Glaubenskriege aus.

Tabs oder Spaces? Linebreak vor { ? ^^

Ich setze mein Votum auch für Dapper. Gerade der "lightweighted" Teil davon ist imho gerade zum wieder einsteigen ganz praktisch.

2+2=5( (für extrem große Werte von 2)

16.834 Beiträge seit 2008
vor 6 Jahren

Ich sehe hier absolut keine pauschale Antwortmöglichkeit, weil es einfach drauf ankommt, was gefordert ist.

Ich bin ein absoluter Fan von Dapper, weil ich eine hohe Performance erreiche, die maximale Kontrolle habe und es keine Blackbox ist.
EF Core würde ich - aktuell - von abraten, einfach weil es kaum Features hat. Es ist zwar schnell, der generierte SQL Code ist aber - siehe GitHub Feedbacks - freundlich gesagt verbesserungswürdig und es kann bis heute keine Stanard-Dinge wie ein Group By.

EF Classic (also Version 6 und davor) war aber nie gut designed, weshalb hier auch quasi die Weiterentwicklung eingestellt wurde.
Die Performance erreichte nie das, was man von EF erwartet hat und konzipiert wurde: das Web. (Warum aber bis heute EF Core zur ASP.NET Entwicklung gehört bzw. angesiedelt ist, ist mir auch ein Rätsel).
Es ist total einfach und super für kleine Desktop-Anwendungen. Wenn man mehr hat, dann wird es schon schwierig.

Mein Fazit: es gibt keine pauschale Antwort, was Du nutzen solltest. Das gibt es bei keiner Technologie.
Es kommt einfach drauf an. JOIN und WHERE hat nichts mit Komplexität zutun; ORM haben sich bei JOINs aber schon immer nicht leicht getan.

F
10.010 Beiträge seit 2004
vor 6 Jahren

Bei Dapper hast du nur den Nachteil das Du SQL schreiben musst, und bei verschiedenen DB Systemen ( hier evtl MS Sql und Firebird ) kann das zu kleinen Problemen führen.

Ich mag Linq2DB weil es einer der schnellsten mapper ist, und durch den T4 Anschluss kannst du den kompletten DAL für vorhandene Datenbanken in 2 Minuten schreiben.

16.834 Beiträge seit 2008
vor 6 Jahren

Ich sehe selbst geschriebenen SQL nicht unbedingt als Nachteil.
Aber es gibt durchaus annehmbare Ansichten, die dies als Nachteil sehen; aber auch dafür gibt es Lösungen zB in Form von FastCrud und SimpleCrud für gewisse Anwendungsfälle.

Ich sehe zB. T4 als extremen Nachteil, weil er mich an einen Editor mit T4 Unterstützung bindet. Würde bei mir/uns das Ausschlusskriterium sein.
Darüber hinaus ist T4 auch kein Bestandteil mehr von Visual Studio (seit 2017) sondern nur noch über das Extension SDK Toolset zu bekommen.

Du siehst: pauschal ist halt schwierig 😉

F
10.010 Beiträge seit 2004
vor 6 Jahren

Ich benutze T4 nur um z.b. bei einem Kunden Initial die DB Struktur in Poco's zu "giessen",
Und dann braucht man es ja nur 1 mal.

Und ich sagte ja oben das es da vieles gibt was man benutzen kann, wenn man genau weiß was man machen will/muss.

L
21 Beiträge seit 2015
vor 6 Jahren

Bei Dapper hast du nur den Nachteil das Du SQL schreiben musst, und bei verschiedenen DB Systemen ( hier evtl MS Sql und Firebird ) kann das zu kleinen Problemen führen. Das führt allerdings auch beim EntityFramework schon zu Problemen, aus dem einfachen Grund das die Provider unterschiedlich "gut" implementiert sind. Ich nutze aktuell auch noch das EntityFramework versuche aber aktuell das so gut es geht aus den Projekten raus zu ziehen.

212 Beiträge seit 2008
vor 6 Jahren

Hallo zusammen,

durch diesen Thread bin ich auf Dapper und Linq2DB aufmerksam geworden. Da mich die Konfiguration vom EntityFramework schon häufiger genervt hat, habe ich mir beide ORMs angeschaut. Da ich die Dokumentation von Dapper deutlich besser finde als die von Linq2DB und ich T4 nicht mag, bin ich schnell bei Dapper gelandet.

Hier gibt es alles was man zu Dapper wissen muss: http://dapper-tutorial.net/

Wenn man sich lange an die Annehmlichkeiten des EntityFramework gewöhnt hat, kommt es schon etwas schräg daher, wenn man wieder SQL schreiben soll. Dank einiger Erweiterungsmethoden für CRUD, durch third party libraries, hält sich der Aufwand allerdings in Grenzen, wie ich finde.

Hier ist eine aktuelle Auflistung mit den aktuellen Erweiterungen: http://dapper-tutorial.net/third-party-library Da muss man einfach schauen was am Besten passt.

Einen Vergleich einiger Libraries gibt es hier: https://blog.falafel.com/implementing-a-generic-repository-with-dapper-extensions/

Hier mal ein Beispiel wie ich Dapper mit Dapper.Contrib einsetze:


public class DapperDemo
    {
        //Steht bei mir in einer Basisklasse
        //==================================             
        private IDbConnection GetConnection()
        {
            //Die Connection kann zentral und simpel ausgetauscht werden, gegen Oracle z.B.
            //wie das im Detail funktioniert wird man dann sehen, bei einfachem CRUD bin ich optimistisch das es gelingt. Aber.......
            return new SqlConnection(SQLConnectionStringProvider.ConnectionString);
        }

        //Funktion die das ständige schreiben des Using-Block vermeidet.
        public T Get<T>(Func<IDbConnection, T> query)
        {
            using (IDbConnection db = GetConnection())
            {
                return query.Invoke(db);
            }
        }
        //Methode die das ständige schreiben des Using-Block vermeidet.
        public void Execute(Action<IDbConnection> query)
        {
            using (IDbConnection db = GetConnection())
            {
                query.Invoke(db);
            }
        }
        //==================================


        //"Native" Dapper mit SQL
        //==================================
        public Result GetByID(string id)
        {
            string sql = "Select * from RESULTS where ID = @ID";

            return Get<Result>(x => x.Query<Result>(sql, new { ID = id }).FirstOrDefault());
        }

        public IEnumerable<Result> GetByCustomer(string customer)
        {
            string sql = "Select * from RESULTS where Customer = @Customer";

            return Get<IEnumerable<Result>>(x => x.Query<Result>(sql, new { Customer = customer }));
        }
        //==================================

            
      
        //CRUD mit Dapper.Contrib als Erweiterung
        //=======================================
        public void InsertNewResult(Result newResult)
        {
            Execute(x => x.Insert<Result>(newResult));
        }
        internal void Update(Result result)
        {
            Execute(x => x.Update<Result>(result));
        }
        public void InsertResultValues(List<ResultValue> listOfresult)
        {
            Execute(x => x.Insert(listOfresult));
        }
        //=======================================

    }

Schade das Dapper.Contrib kein Get mit einer Where Klausel anbietet, dann wäre es perfekt.

Und hier die Verwendung:


static void Main(string[] args)
        {
            DapperDemo dd = new DapperDemo();

            //Insert
            //======================================
            Result newRes = new Result()
            {
                ID = DateTime.Now.ToString(),
                Customer = "Christoph"                
            };
            dd.InsertNewResult(newRes);
            //======================================

            //Select
            //======================================
            Result itemByID = dd.GetByID("1234");

            IEnumerable<Result> resultsByCustomer = dd.GetByCustomer("Christoph");
            //======================================

            //Update
            //======================================
            Result resToUpdate = dd.GetByID("1234");
            resToUpdate.Description = "Updated now";
            dd.Update(resToUpdate);
            //======================================
        }

Zum generieren der POCOs verwende ich aktuell diesen POCO Generator: https://www.codeproject.com/Articles/892233/POCO-Generator

Bisher bin ich wirklich begeistert und werde mein nächstes Projekt mit Dapper realisieren. Die Verwendung ist wirklich simpel, die Vorteile der Geschwindigkeit konnte ich noch nicht beurteilen, wobei mir da das EntityFramework aber auch immer völlig gereicht hat.

Wie sind eure Erfahrungen, gibt es unbekannte Schwächen, wie implementiert ihr Dapper?

Gruß
Christoph

16.834 Beiträge seit 2008
vor 6 Jahren

Sieht bei meiner Basis von Dapper ein wenig anders aus..

zB ist folgendermaßen mein IDBContext, mit dem dann die Repositories arbeiten:

    public interface IDbContext
    {
        Task<T> ExecuteAsync<T>(Func<DbConnection, Task<T>> action);
        Task ExecuteAsync(Func<DbConnection, Task> action);

        void Execute(IEnumerable<Action<DbConnection>> actions);
        void Execute(Action<DbConnection> action);

        Task<TEntity> ExecuteScalarAsync<TEntity>(string statement, object values = null);

        Action<DbConnection> CreateAction(string statement, object values = null);
        Func<DbConnection, Task> CreateAsyncAction(string statement, object values);


        Task<TEntity> QueryAsync<TEntity>(string statement, object values = null);
        TEntity QuerySync<TEntity>(string statement, object values = null);
        Task<IList<TEntity>> QueryManyAsync<TEntity>(string statement, object values = null);

        IList<TEntity> QueryManySync<TEntity>(string statement, object values = null);
    }

Das Erstellen einer Action(schreibend)/Query(lesend) übernimmt dann die Implementierung, wobei der DapperDbContext so gestaltet ist, dass man Eigenheiten wie bei allen ADO.NET-Möglichkeiten sehr einfach injezieren kann.

    public abstract class DapperDbContext : IDbContext
    {
        public abstract DbConnection OpenConnection();

        private DapperDbContext() { Dapper.SqlMapper.AddTypeMap(typeof(Guid), System.Data.DbType.AnsiString); }

        public string Connectionstring { get; }

        protected DapperDbContext(string connectionstring) : this() { Connectionstring = connectionstring; }

        public Task<T> ExecuteAsync<T>(Func<DbConnection, Task<T>> asyncAction)
        {
            using (var connection = OpenConnection()) { return asyncAction(connection); }
        }
        public void Execute(IEnumerable<Action<DbConnection>> actions)
        {
            using (var connection = OpenConnection())
            using (var transaction = connection.BeginTransaction())
            {
                foreach (var action in actions) { action(transaction.Connection); }
                transaction.Commit();
            }
        }

        public Func<DbConnection, Task> CreateAsyncAction(string statement, object values)
        {
            return (dbConnection) => dbConnection.ExecuteAsync(statement, values);
        }

Die Verwendung in einem Repository sieht dann so aus:

protected DapperBaseRepository(IDbContext dbContext, string tableName)
        {
            TableName = tableName;
            DbContext = dbContext;
        }

        public override async Task<TEntity> GetByIdAsync(Guid id)
        {
            string statement = $"SELECT * FROM {TableName} WHERE [Id] = @EntityID";

            return await DbContext.QueryAsync<TEntity>(statement, new { EntityID = id });
        }
212 Beiträge seit 2008
vor 6 Jahren

Wie sind eure Erfahrungen, gibt es unbekannte Schwächen

Dapper.Contrib unterstützt leider kein Oracle, schade :-|

Gruß
Christoph