私がノルム対ダッパーを使うのを好む3つの理由
Norm data-access library for .NET 楽しいペットプロジェクトとしてスタートした(これらのHomebrew MicroOmのプロジェクトの多くがそうであるように)、そして、今、私はそれが最新のバージョン3.1でその満期に達したと思っています.
すべてのこれらの小さなMicroOmの共有は、同じ基本的な機能を、例えば、すべてのSQLクエリの結果を選択したデータ構造にマップされます.
例えば、以下のようなクラスがあるならば:
同様に、ノルムの例は次のようになります.
それは実に非常に似ている.
しかし、私にとっては、少なくとも3つの良い理由があります.なぜ私のプロジェクトでdapperの上でNORMを使用したいか.
はい、どうぞ.
1すべてのモデルクラスを持っていない.
私は、単に新しいクラス(またはより新しいバージョンの記録)モデルを作成するのに使用されると思います.
それは常に行われた方法です.
もちろん、すべてのクエリがモデルクラスを持っている必要はありません.典型的な例としては、例えばcountクエリーです.
その場合、モデルクラスを作成することは、まだ私の意見においては過大です.
例えば、ミニマムと言いましょう
クエリは次のようになります.
これらの2つの値は、表示される位置によってマップされ、名前はこの場合完全に無関係です.
これはきちんとしています.なぜなら、私たちが単に2、3の値を必要とするなら、私たちは本当にクラスを作成する必要はありません.
例えば、3つの値、製品名、製品単位、および製品価格:
一般的に、上記の例では、これらの3つの値を必要とするだけで、おそらくいくつかの出力やいくつかのアルゴリズムでは、モデルクラスを作成したくないかもしれません.
ここでは、辞書キーが製品IDであり、辞書値が製品名である製品のデータベースから辞書を作りたいと言いましょう.
標準式は次のようになります.
これは私たちがフィールド名を失ったので、少しsuboptimalです
複雑なLINQ式では、これは重大な問題でありえます.
これはnormが名前付きタプルをサポートする理由です.
したがって、上記の例は次のようになります.
これは今ではずっと良いです.なぜなら、私たちにはまだ強いタイプがあります.また、完全なIDEとIntelliSenseエディタのサポートとともに、適切な名前も持っています.
そして、我々はまだモデルクラスを作成する必要はありませんでした.
しかし、もちろん、あなたが本当にしたい場合.
そして、おそらくフィールドはもはや位置によってマップされないが、むしろ名前によってマップされるので、それはおそらく賢い決定です.
2 .デフォルトでの列挙発電
Dapperに慣れている人は、Dapperのクエリメソッドが基本的に二つの異なるバージョンを持っていることを覚えておいてください. buffered (デフォルト): dapperは結果からリスト全体を構築し、クエリメソッドを返します. Unbuffered : Dapperは実際の反復の結果を生成する列挙型ジェネレータを作成し、クエリメソッドは実際のリスト構造の代わりにそのジェネレータを返します. バッファされたバージョンは、いくつかの理由のための準最適解です.例えば、 同じデータ上の複数の反復の可能性.一度実際のリストと2番目の時間をビルドしてコード内のデータを使用して(UIをレンダリングし、サービスの結果などを生成する). あなたが最初の場所でリストを構築する必要はありません.何らかの理由で配列や辞書を使うことを好むかもしれません. 結果から最初の行だけを取得したい場合は、最初の行の後に結果リストをビルドする反復を停止する方法はありません.このデザイン決定の結果として、Dapperは最初の行の後にイテレータ停止を作る別々の方法を持たなければなりません.それが方法 今、私はこの決定の背後にある本当の理由は、非バッファのクエリの前に非同期バージョンを実装する方法がなかったからです.NET標準2.1 .および.NETコア3
あなたが戻らなければならないから
したがって、同期バージョンのクエリをバッファリングされていないメソッドとasyncだけでバッファリングされたバージョンでのみ良い妥協したと信じています.
一方、規範は厳密です.NET標準2.1 .図書館.つまり、以下のようにサポートされます.NETの実装 .ネット5 .NETコア3.0と3.1 モノ6.4 ザマリンIOSの12.16 ザマリンアンドロイド10.0 そして、そのように、それはバッファなしのバージョンを実装するだけです.それは、ノルム
これを行う決定は本当に簡単だった.サポートされていないバージョン(例えば. NET Frameworkなど)で同じ機能を使用する場合は、常にdapperを使用できます.それはあなたが必要とするほとんどすべてをカバーします.
例えば、最初の行だけをNORMでyieldするには、LINQ拡張子を使用できます
dapperでコードを実行すると、結果セット全体が反復され、最初の結果が返されます.
非同期バージョンでは、標準のLINQ式は
解決策は
このライブラリは、言語統合クエリ
これは、標準のLINQライブラリと同じ拡張機能を実装します.
これは、作成され、";ネット財団と貢献者";でサポートされており、それは3以上、これまでの百万ダウンロードがあります.
また、同じ名前空間を使用します.
したがって非同期
これは非同期ストリーミングです.ノルムを使用すると、非同期で反復することができます
これは、すぐに彼らが表示されるように接続から結果を得ることができますし、より良いパフォーマンスとスケーラビリティを与える非同期のストリーミング.
PostgreSQL
そして最後に、私を幸せにした小さなもの.
私はかなり重いPostgreSQLユーザです、そして、それは選択の私のデータベースです.
したがって、PostgreSQL開発者のための重要な機能は、追加の設定なしでボックスから利用できます. スネークケースマッピング.クラスまたはレコードへのマッピング時には、標準であなたのクエリにあるすべてのスネークケースの名前をマップします. 配列型サポート.PostgreSQLデータベースクエリが配列を返す場合、問題はありません.
すべてのこれらの小さなMicroOmの共有は、同じ基本的な機能を、例えば、すべてのSQLクエリの結果を選択したデータ構造にマップされます.
例えば、以下のようなクラスがあるならば:
public class OrderDetail
{
public int OrderDetailID { get; set; }
public int OrderID { get; set; }
public int ProductID { get; set; }
public int Quantity { get; set; }
}
そして、クエリから結果をマップしたかったSELECT TOP 10 * FROM OrderDetails
, Dapperの例は、一般的に次のようになります.using var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools());
var sql = "SELECT TOP 10 * FROM OrderDetails";
var orderDetails = connection.Query<OrderDetail>(sql);
// output the results
Console.WriteLine(orderDetails.Count());
FiddleHelper.WriteTable(orderDetails);
Try it yourself 同様に、ノルムの例は次のようになります.
using var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools());
var sql = "SELECT TOP 10 * FROM OrderDetails";
var orderDetails = connection.Read<OrderDetail>(sql);
// output the results
Console.WriteLine(orderDetails.Count());
FiddleHelper.WriteTable(orderDetails);
Try it yourself それは実に非常に似ている.
しかし、私にとっては、少なくとも3つの良い理由があります.なぜ私のプロジェクトでdapperの上でNORMを使用したいか.
はい、どうぞ.
1すべてのモデルクラスを持っていない.
私は、単に新しいクラス(またはより新しいバージョンの記録)モデルを作成するのに使用されると思います.
それは常に行われた方法です.
もちろん、すべてのクエリがモデルクラスを持っている必要はありません.典型的な例としては、例えばcountクエリーです.
SELECT COUNT(*)
FROM OrderDetails
このような場合、DapperはQuery
. 例えば、var sql = "SELECT COUNT(*) FROM OrderDetails";
var count = connection.QuerySingle<int>(sql);
NORMはまだ同じRead
メソッドですが、LINQ拡張モジュールを使用しますSingle
最初の結果を得るには、次の手順に従います.var sql = "SELECT COUNT(*) FROM OrderDetails";
var count = connection.Read<int>(sql).Single();
さて、1つではなく2つの値を返すクエリから結果を得たい場合はどうでしょうか.その場合、モデルクラスを作成することは、まだ私の意見においては過大です.
例えば、ミニマムと言いましょう
OrderID
そのIDの総カウント値.クエリは次のようになります.
SELECT TOP 1 min(OrderID) as OrderID, COUNT(*)
FROM OrderDetails
GROUP BY OrderID
これはDAPPERで可能ですが、マルチマッピング機能を使用しなければなりません.var sql = @"
SELECT TOP 1 min(OrderID) as OrderID, COUNT(*)
FROM OrderDetails
GROUP BY OrderID
";
var (orderId, count) = connection.Query<int, int, (int, int)>(sql,
(orderId, count) => (orderId, count),
splitOn: "OrderID").Single();
ノルムは、まだ同じを使用しますRead
メソッド:var sql = @"
SELECT TOP 1 min(OrderID), COUNT(*)
FROM OrderDetails
GROUP BY OrderID
";
var (orderId, count) = connection.Read<int, int>(sql).Single();
この場合、min(OrderID)
NormはAを必要としないので、名前別名を持っていませんsplitOn
値をnameで区別するパラメータです.これらの2つの値は、表示される位置によってマップされ、名前はこの場合完全に無関係です.
これはきちんとしています.なぜなら、私たちが単に2、3の値を必要とするなら、私たちは本当にクラスを作成する必要はありません.
例えば、3つの値、製品名、製品単位、および製品価格:
var sql = "SELECT TOP 10 ProductName, Unit, Price FROM Products";
foreach(var (name, unit, price) in connection.Read<string, string, decimal>(sql))
{
Console.WriteLine($"{name} has price {price} per {unit}");
}
Try it yourself 一般的に、上記の例では、これらの3つの値を必要とするだけで、おそらくいくつかの出力やいくつかのアルゴリズムでは、モデルクラスを作成したくないかもしれません.
ここでは、辞書キーが製品IDであり、辞書値が製品名である製品のデータベースから辞書を作りたいと言いましょう.
標準式は次のようになります.
var sql = "SELECT TOP 10 ProductID, ProductName FROM Products";
var dict = connection.Read<int, string>(sql).ToDictionary(p => p.Item1, t => p.Item2);
Try it yourself これは私たちがフィールド名を失ったので、少しsuboptimalです
Item1
and Item2
名称複雑なLINQ式では、これは重大な問題でありえます.
これはnormが名前付きタプルをサポートする理由です.
したがって、上記の例は次のようになります.
var sql = "SELECT TOP 10 ProductID, ProductName FROM Products";
var dict = connection.Read<(int Id, string Name)>(sql).ToDictionary(p => p.Id, t => p.Name);
Try it yourself これは今ではずっと良いです.なぜなら、私たちにはまだ強いタイプがあります.また、完全なIDEとIntelliSenseエディタのサポートとともに、適切な名前も持っています.
そして、我々はまだモデルクラスを作成する必要はありませんでした.
しかし、もちろん、あなたが本当にしたい場合.
そして、おそらくフィールドはもはや位置によってマップされないが、むしろ名前によってマップされるので、それはおそらく賢い決定です.
2 .デフォルトでの列挙発電
Dapperに慣れている人は、Dapperのクエリメソッドが基本的に二つの異なるバージョンを持っていることを覚えておいてください.
QuerySingle
and QueryFirst
用です.あなたが戻らなければならないから
IAsyncEnumerable
の代わりにTask<IEnumerable>
それを達成するために、そのインターフェースはそれらのバージョンの前に存在しませんでした.したがって、同期バージョンのクエリをバッファリングされていないメソッドとasyncだけでバッファリングされたバージョンでのみ良い妥協したと信じています.
一方、規範は厳密です.NET標準2.1 .図書館.つまり、以下のようにサポートされます.NETの実装
Read
メソッドは、実際の反復の結果を生成する列挙型ジェネレータを作成して返します.これを行う決定は本当に簡単だった.サポートされていないバージョン(例えば. NET Frameworkなど)で同じ機能を使用する場合は、常にdapperを使用できます.それはあなたが必要とするほとんどすべてをカバーします.
例えば、最初の行だけをNORMでyieldするには、LINQ拡張子を使用できます
Single
:var sql = "SELECT COUNT(*) FROM OrderDetails";
var count = connection.Read<int>(sql).Single();
Single
最初の読み込み後に反復処理を停止します.dapperでコードを実行すると、結果セット全体が反復され、最初の結果が返されます.
var sql = "SELECT OrderID FROM OrderDetails";
var count = connection.Query<int>(sql).Single();
そのパフォーマンスの落とし穴を回避するには、バッファー付きのバージョンに戻る必要があります.var sql = "SELECT OrderID FROM OrderDetails";
var count = connection.Query<int>(sql, buffered: false).Single();
しかし、デフォルトでバッファなしのバージョンでは、他の利点があります.たとえば、辞書や配列などの他のデータ構造を好み、リスト部分を完全にスキップします.var sql = "SELECT OrderID FROM OrderDetails";
var orderIds = connection.Read<int>(sql).ToArray();
またはdapperがあります:var sql = "SELECT OrderID FROM OrderDetails";
var orderIds = connection.Query<int>(sql, buffered: false).ToArray();
Normがすべての読み込み操作のために1つの拡張/メソッドだけを持つことができる理由Read
.非同期バージョンでは、標準のLINQ式は
IEnumerable
インタフェースではなくIAsyncEnumerable
.解決策は
System.Linq.Async
プロジェクトに.このライブラリは、言語統合クエリ
IAsyncEnumerable<T>
シーケンス.これは、標準のLINQライブラリと同じ拡張機能を実装します.
これは、作成され、";ネット財団と貢献者";でサポートされており、それは3以上、これまでの百万ダウンロードがあります.
また、同じ名前空間を使用します.
したがって非同期
Single
上記の例は次のようになります.var sql = "SELECT COUNT(*) FROM OrderDetails";
var count = await connection.Read<int>(sql).SingleAsync();
しかし、あなたがそうすることができる1つの強力なasync機能もありますIAsyncEnumerable<T>
結果.これは非同期ストリーミングです.ノルムを使用すると、非同期で反復することができます
foreach
結果について例えば、// Asynchronously stream values directly from database
var sql = "SELECT TOP 10 OrderId, ProductId, Quantity FROM OrderDetails";
await foreach(var (orderId, productId, quantity) in connection.ReadAsync<int, int, int>(sql))
{
Console.WriteLine($"order={orderId}, product={productId} with quantity {quantity}");
}
Try it yourself これは、すぐに彼らが表示されるように接続から結果を得ることができますし、より良いパフォーマンスとスケーラビリティを与える非同期のストリーミング.
PostgreSQL
そして最後に、私を幸せにした小さなもの.
私はかなり重いPostgreSQLユーザです、そして、それは選択の私のデータベースです.
したがって、PostgreSQL開発者のための重要な機能は、追加の設定なしでボックスから利用できます.
int[]
, string[]
, DateTime[]
, など).単純な型、タプル、またはクラスとレコードのマッピング時と同じように動作します.Reference
この問題について(私がノルム対ダッパーを使うのを好む3つの理由), 我々は、より多くの情報をここで見つけました https://dev.to/vbilopav/3-reason-why-i-prefer-using-norm-vs-dapper-4p9cテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol