はい、SQL文字列をアプリケーションコードにハードコーディングすることは、一般にアンチパターンです。
本番コードでこれを長年にわたって見てきたことから、私たちが開発した許容値を別にしてみましょう。同じファイルに完全に異なる言語と異なる構文を混在させることは、一般的に望ましい開発手法ではありません。これは、複数の言語に文脈上の意味を与えるように設計されたRazorのようなテンプレート言語とは異なります。以下のようサヴァB.は、以下のコメントに言及し、あなたのC#または他のアプリケーション言語(PythonやC ++など)でのSQLは、他のどのような文字列で、意味的に無意味です。ほとんどの場合、複数の言語を混合する場合にも同じことが当てはまりますが、Cのインラインアセンブリ、HTMLのCSSの小さくてわかりやすいスニペット(CSSはHTMLと混合するように設計されていることに注意してください) )、 その他。
(混合言語のロバートC.マーティン、クリーンコード、第17章「コードの匂いとヒューリスティック」、288ページ)
この応答では、SQLに焦点を当てます(質問で尋ねたとおり)。関連付けられていない文字列のアラカルトセットとしてSQLを保存する場合、次の問題が発生する可能性があります。
- データベースロジックを見つけるのは困難です。すべてのSQLステートメントを見つけるために何を検索しますか?「SELECT」、「UPDATE」、「MERGE」などの文字列?
- 同じまたは類似のSQLの使用をリファクタリングすることは難しくなります。
- 他のデータベースのサポートを追加することは困難です。これをどのように達成できますか?各データベースにif..thenステートメントを追加し、すべてのクエリをメソッドに文字列として保存しますか?
- 開発者は別の言語のステートメントを読み、メソッドの目的からメソッドの実装の詳細(データの取得方法および取得元)へのフォーカスのシフトに気を取られます。
- ワンライナーはそれほど大きな問題ではないかもしれませんが、ステートメントがより複雑になるにつれて、インラインSQL文字列はバラバラになり始めます。113行のステートメントで何をしますか?メソッドに113行すべてを入れますか?
- 開発者は、SQLエディター(SSMS、SQL Developerなど)とソースコードの間でクエリを効率的にやり取りする方法を教えてください。C#の
@
プレフィックスはこれを簡単にしますが、各SQL行を引用して改行をエスケープするコードをたくさん見ました。
"SELECT col1, col2...colN"\
"FROM painfulExample"\
"WHERE maintainability IS NULL"\
"AND modification.effort > @necessary"\
- SQLを周囲のアプリケーションコードに合わせるために使用されるインデント文字は、実行のたびにネットワーク経由で送信されます。これはおそらく小規模なアプリケーションにとっては重要ではありませんが、ソフトウェアの使用量が増えるにつれて増える可能性があります。
完全なORM(Entity FrameworkやHibernateなどのオブジェクトリレーショナルマッパー)を使用すると、アプリケーションコード内のランダムにペッパーSQLを削除できます。SQLとリソースファイルの私の使用はほんの一例です。ORM、ヘルパークラスなどはすべて、よりクリーンなコードの目標を達成するのに役立ちます。
Kevinが以前の回答で述べたように、コード内のSQLは小さなプロジェクトで受け入れられる可能性がありますが、大規模なプロジェクトは小さなプロジェクトとして始まり、ほとんどのチームが戻って適切に実行される確率は、コードサイズに反比例することがよくあります。
SQLをプロジェクトに保持するための多くの簡単な方法があります。私がよく使用する方法の1つは、通常「sql」という名前のVisual Studioリソースファイルに各SQLステートメントを配置することです。ツールによっては、テキストファイル、JSONドキュメント、またはその他のデータソースが妥当な場合があります。場合によっては、SQL文字列を固定するための専用のクラスが最適なオプションかもしれませんが、上記の問題のいくつかが発生する可能性があります。
SQLの例:どちらがよりエレガントに見えますか?:
using(DbConnection connection = Database.SystemConnection()) {
var eyesoreSql = @"
SELECT
Viewable.ViewId,
Viewable.HelpText,
PageSize.Width,
PageSize.Height,
Layout.CSSClass,
PaginationType.GroupingText
FROM Viewable
LEFT JOIN PageSize
ON PageSize.Id = Viewable.PageSizeId
LEFT JOIN Layout
ON Layout.Id = Viewable.LayoutId
LEFT JOIN Theme
ON Theme.Id = Viewable.ThemeId
LEFT JOIN PaginationType
ON PaginationType.Id = Viewable.PaginationTypeId
LEFT JOIN PaginationMenu
ON PaginationMenu.Id = Viewable.PaginationMenuId
WHERE Viewable.Id = @Id
";
var results = connection.Query<int>(eyesoreSql, new { Id });
}
になる
using(DbConnection connection = Database.SystemConnection()) {
var results = connection.Query<int>(sql.GetViewable, new { Id });
}
SQLは常に見つけやすいファイルまたはファイルのグループ化されたセットにあり、各ファイルには、それがどのように実行されるかを説明する説明的な名前があり、それぞれにアプリケーションコードのフローを中断しないコメント用のスペースがあります:
![リソース内のSQL](https://i.stack.imgur.com/Qb7GX.png)
この単純なメソッドは、単独のクエリを実行します。私の経験では、「外国語」の使用がより洗練されるにつれて、利益は拡大します。
リソースファイルの私の使用は単なる例です。言語(この場合はSQL)とプラットフォームに応じて、異なる方法がより適切な場合があります。
このメソッドおよび他のメソッドは、次の方法で上記のリストを解決します。
- データベースコードは既に集中化されているため、簡単に見つけることができます。大規模なプロジェクトでは、like-SQLをおそらくという名前のフォルダーの下の個別のファイルにグループ化します
SQL
。
- 2番目、3番目などのデータベースのサポートは簡単です。各データベースの一意のステートメントを返すインターフェイス(または他の言語の抽象化)を作成します。各データベースの
return SqlResource.DoTheThing;
実装は、次のようなステートメントにすぎません:True、これらの実装はリソースをスキップしてSQLを文字列に含めることができますが、上記の(すべてではない)いくつかの問題が依然として表面化します。
- リファクタリングは簡単です-同じリソースを再利用するだけです。いくつかのフォーマットステートメントを使用すれば、多くの場合、さまざまなDBMSシステムに同じリソースエントリを使用できます。私はこれを頻繁に行います。
- 二言語の使用は、使用することができますわかりやすい名前など
sql.GetOrdersForAccount
ではなく、多くの鈍角をSELECT ... FROM ... WHERE...
- SQLステートメントは、サイズと複雑さに関係なく1行で呼び出されます。
- SQLは、SSMSやSQL Developerなどのデータベースツール間で、変更したり慎重にコピーしたりすることなくコピーして貼り付けることができます。引用符なし。末尾のバックスラッシュはありません。特にVisual Studioリソースエディターの場合、1回クリックするとSQLステートメントが強調表示されます。Ctrl + Cキーを押して、SQLエディターに貼り付けます。
リソースでのSQLの作成は迅速であるため、リソースの使用とSQL-in-codeを混在させる推進力はほとんどありません。
選択した方法に関係なく、言語を混在させると通常コードの品質が低下することがわかりました。ここで説明するいくつかの問題と解決策が、開発者が適切なときにこのコードの臭いをなくすのに役立つことを願っています。