web-dev-qa-db-ja.com

これは良い習慣ですか?

データベースから情報を「一般化」する方法を考え出した同僚がいるため、彼のすべてのWebアプリケーションのドロップダウンリストが、MVC.NET C#コードとビューで同じオブジェクトを共有できます。どのテーブルに対して使用されているか。

私たちは政府機関で働いており、「リージョン」と呼ばれるエリアに分割された施設があり、VISNと呼ばれるさらに細分化されたエリアがあり、それぞれに「サイト」または「施設」が含まれています。私の同僚は非常に複雑なセキュリティスキームを開発しました。これにより、地域、VISN、サイト、またはこれら3つを組み合わせて割り当てることができる権限レベルに基づいて、データへのアクセスを許可できます。それは素晴らしい計画であり、非常に賢いです。ただし、彼は、ユーザーのユーザーIDに基づいてリージョン、VISN、およびサイトのリストを返すストアドプロシージャを使用しており、TextFieldIDTextFieldTextParentID。これに関する私の最初の問題は、データベースから出てくるこのデータを見て、データが何であるかわからないということです。クエリまたはストアドプロシージャからのフィールドは、それらが提供するデータを説明するものである必要があると思います。他の誰もがどう思いますか?しかし、私にとってより深い問題は、彼がデータの一部を取得して、次のようなストアドプロシージャに連結していることです。

SELECT DISTINCT
    t.VisnID,
    NULL,
    t.StationID,
    'V' + CAST(t.VisnID as varchar) + ': ' + t.Station3N + ': ' + t.StationName,
    t.Inactive
FROM Stations t 

個別のデータを個別に送り返すのではなく、「TextField」プロパティで送り返します(Station3NStationName)およびビューでの連結。これにより、アプリケーションにアクセスしているデバイス(おそらくモバイルおよびデスクトップ)に応じて、さまざまな連結が可能になります。彼の正当な理由は、彼がさまざまなドロップダウンデータをすべて送信し、内容に関係なく、すべてを "LookupValue"という名前の同じC#オブジェクトにキャプチャできることです。

public partial class LookupValue : IEquatable<LookupValue>
{
    public LookupValue(string textFieldId, string textField, bool inactive)
    {
        TextFieldID = textFieldId;
        TextField = textField;
        Inactive = inactive;
    }

    public LookupValue(string textParentId, string textFieldId, string textField, bool inactive)
    {
        TextParentID = textParentId;
        TextFieldID = textFieldId;
        TextField = textField;
        Inactive = inactive;
    }

    public LookupValue(string textParentId, string textSelfParentId, string textFieldId, string textField, bool inactive)
    {
        TextParentID = textParentId;
        TextSelfParentID = textSelfParentId;
        TextFieldID = textFieldId;
        TextField = textField;
        Inactive = inactive;
    }

    /// <summary>
    /// Returns a custom string identifier if the Inactive property is true.
    /// </summary>
    public string GetInactiveString()
    {
        string value = "";
        if (Inactive)
        {
            value = "[I]";
        }

        return value;
    }

    /// <summary>
    /// Returns a custom text label that concatenates the TextField property with the custom string identifier of the Inactive property.
    /// </summary>
    public string GetDisplayNameWithInactiveString
    {
        get { return TextField + " " + GetInactiveString(); }
    }

    public bool Equals(LookupValue other)
    {
        //Check whether the compared object is null.  
        if (Object.ReferenceEquals(other, null)) return false;

        //Check whether the compared object references the same data.  
        if (Object.ReferenceEquals(this, other)) return true;

        //Check whether the products' properties are equal.  
        return TextFieldID.Equals(other.TextFieldID) && TextField.Equals(other.TextField);
    }

    // If Equals() returns true for a pair of objects
    // then GetHashCode() must return the same value for these objects.
    public override int GetHashCode()
    {
        //Get hash code for the TextFieldID field if it is not null.  
        int hashTextFieldId = TextFieldID == null ? 0 : TextFieldID.GetHashCode();

        //Get hash code for the Code field.  
        int hashTextField = TextField.GetHashCode();

        //Calculate the hash code for the product.  
        return hashTextFieldId ^ hashTextField;
    }
}

彼は、このオブジェクトの再利用性は、懸念の分離の違反、およびドロップダウンの複数の表示バリエーションを処理する際の将来の困難の可能性に値すると考えています。このオブジェクトは、Webプロジェクトの名前空間ではなく、Dataプロジェクトの名前空間(プロジェクトはData、Web、Servicesなどに分離されています)に含まれており、リポジトリクエリを介してこのオブジェクトをWebレイヤーに返します。以前に説明したストアドプロシージャを呼び出します。これは、私の意見では懸念の分離に対する重大な違反です。

これは実際には悪い習慣であるという他のプログラマーからの確認を探しています。また、彼が試みていることを実行するためのより良い方法について彼に提示できるアイデアを探しています。私は自分のアイデアを持っていますが、ここでも他のアイデアを探しているので、彼に提示するオプションが増えます。

-編集-Entity FrameworkをORMとして使用しており、DALの彼のリポジトリクラスがストアドプロシージャからこのLookupValueオブジェクトにデータをダンプしていることを忘れていました。

4
JasonAlun

30年以上ソフトウェアの作成とI.Tでの作業を経験した後、データベースからのデータを連結する正当な理由を見つけたことはありません。

アプリケーションコードの90%をDBインフラストラクチャに配置し、それを使ってあらゆる種類の魔法のことを行っている可能性がありますが、それでも良い考えだとは思いもしません。

すでに示唆したように、データベースは特定の1つのタスク、およびデータの格納、検索、取得、および一般的な管理のための1つの特定のタスクのみのために設計されています。

メタデータ、インデックス、クラスターなど、DBMSが得意とするすべてのことは、DBMSが可能な限り最高の効率でその仕事を行うことを保証するためにあり、それを回避しようとすると、トラブルの山に身を投じることになります。

このように見てください。コンパイラよりも優れた結果を得るために、独自のC#コードを取得して手動でコンパイルしようとしますか?あなたはもっと良い結果を得ることができますか?同じトークンで、独自のデータスキームを作成することは無謀で無責任な私見です。

とは言っても、あなたの同僚には理由があるのか​​もしれません。他の人が言ったように、あなたが秘密にしていない要素があるかもしれません。

彼が考慮しなければならないことは、時事問題に対する彼の食い込みの影響だけでなく、それが将来のために保持することです。

のようなもの:

  • 1)将来のメンテナンス。彼は10年後もそこにいるのでしょうか、それとも1年後でしょうか?そうでない場合、誰がそれを維持するのですか?

  • 2)彼が去る場合、そして彼が悪い状況下で去ると、誰かが彼のシステムがどのように機能するかについて訓練されるでしょうか?または、上級管理職は、彼と一緒に働いたすべての人がこのコードを理解していると仮定しますか? (私からそれを取りなさい、私は何度もこの立場にいたことがあります。あなたが見ているものの最初の手掛かりがなければ、それはおかしくありません。)

  • 3)HTML仕様が変更されて連結が将来のリビジョンに違反する場合(たとえば、保護された文字シーケンスを使用して連結するため)、または将来の仕様変更により連結が切断される場合はどうなりますか? (このシナリオは、あなたが理解しているよりもすでに何度も発生しています。)

  • 4)データベースサーバーが一気にダウンした場合、どちらが回復しやすいですか?誰もが理解できる標準に準拠したデータベース構造か、ストアドプロシージャとカスタムデータスキームの3つの見出しがひどく難読化されていますか? (私はどちらを選ぶか知っています。)

私はここに座って、あなたにもっと多くの理由を簡単に与えることができましたが、私は行くつもりはありません。

代わりに、私はあなたたち2人を幸せに保つための方法を提案します。

彼はもっとよく知らないので、このように物事をしているのかもしれません。

彼はDEVよりもDBAの方がいいと言っていますが、それは結構です。私はDBAの素晴らしい人たちと協力してきましたが、彼らの前にコンパイラーを置くと、彼らはボールを丸めて泣き始め、幸せになります彼らの知識の欠如を認める。

同様に、私はDBAと協力してきました。非常に頑固で、何らかの理由で物事を行う方法を知らないため、その構造を最終的に適用します。多くの場合、彼らは何が起こっているのか本当に理解していないためです。 DBA、しかし私たちの多くがそうしたくないように(人間の性質はそれが何であるかです...)

あなたがそれをどのように見ても、彼は動揺することはありません、そしてあなたはあなたの「正しくそれをしなさい、内側の開発者」が鎮静されるまであなたは幸せになりません。

必要なのは妥協です。

私の考えでは、ビューを使用してこの妥協を簡単に達成できます。

彼にすべてのロジックを一連のビューに入れさせることができるかどうかを確認します。このアプローチを必要とするコードは、既存のテーブルやストアドプロシージャをクエリするのと同じくらい簡単に、それらをクエリできます。

このようにして、実際のテーブル定義は、個別の列を持つ初期状態のままなので、それらに対して標準の準拠ORMベースのコードを記述できます。

このアプローチは、地理データを処理するためのアプリケーションの作成を任された、あまり似ていないシナリオで過去に非常にうまく使用しました。

私の特定のケースでは、DBテーブルからデータを取得する必要がありましたが、このテーブルは、別のアプリに適した形式でデータを返すために、バックエンドで前処理されています。ここでの私の解決策は、ビューを使用して、そのデータを私のタスクに必要なものに戻すことでした。

これを行うことで、元のインターフェースとプログラムコードは機能し続けましたが、必要な作業の一部をより柔軟に変更できる柔軟性が得られました。

たとえば、彼がDBとそのアクセスを制御しているために、DBレベルでこのアプローチをとることができない場合は、他の方法に頼る必要があるかもしれません。

あなたが言うとおりにC#で作業している場合は、コードを別のモジュールにフックすることは難しくありません。

.NETコードは、どの言語で記述されていても、単純なMSILにコンパイルされ、そこに独自のコードを挿入して、すでに実行されているものを置き換える方法は何十通りあります。

上記のRobert Harveyは、Automapperなどの製品を使用して言及しました。

これはかなり合理的な提案ですが、考えてみてください。

私はいつもAutomapperを使っており、Automapperはそのために設計された仕事に適しています。その仕事は、N層デザインのレイヤー間でDTOを渡し、一致させることです。

Automapperは通常、複数のDTOをフラット化して単一レベルのビューモデルに結合するために使用されます。通常、MVCやMVVMなどのプレゼンテーションレイヤー駆動パターンで使用されます。

ただし、この特定のケースでは、Automapperがどのように役立つかはわかりません。 AMは規則に基づいています。これは、たとえば、片側に次のようなEFオブジェクトがある場合、

public class EFobject
{
  public string name { get; set; }
  public string email { get; set; }
  public string dept { get; set; }
}

反対側の通常のPOCOは次のようになります。

public class RegularPoco
{
  public string name { get; set; }
  public string email { get; set; }
}

オートマッパーは、名前と電子メールのフィールドが確実にコピーされるようにしますが、deptフィールドは無視します。

コードレベルで文字列を連結している場合、Automapperを簡単に使用して、同僚が生成しているのと同じ結果を生成できます。これらの3つの列を使用するマッピングルールを指定し、それらの間に「:」記号を付けて1つの列にプッシュします。各。しかし、それらを分解すると、AMはその仕事に適したツールではありません。

あなたが持っている別のオプションは、あなたのビジネスレイヤーでこれを処理することです。

優れたN層アーキテクチャと設計原則に従っている場合、最低限、「プレゼンテーション」、「ビジネスルール」、「データ」の各レイヤーが必要です。

私自身も、通常はDTOモデルとビューモデルを別々のレイヤーに分離していますが、これはVisual Studioでの作業方法を支援するためだけのものです。

ビジネスルールレイヤーを使用すると、最初にそのレイヤーを通過せずにプレゼンテーションレイヤーになることはありません。その時点で、.NETエコスフィア全体を自由に使用して、その文字列を再びバラバラにすることができます。

ただし、ご指摘のとおり、元のデータが何であるかがわからない場合は役に立ちません。そのため、これに対処するためのアプローチがもう1つあります。

彼の習慣を破ることができず、変化に抵抗する場合は、「賛美に基づく変化」を使用します。

ビジネスルールレイヤーでダイジェストできるデータを追加することにより、彼のアプローチを称賛し、参考になり、彼のやり方を「改善する」ことを提案します。たとえば、出力を次のようなものに変更してもらいます。

1234:station1:fred

1234(stationid,int):station1(stationref,string):fred(stationname,string)

彼の選択主導の執着では、単純な検索を介して単純に物事を渡し、上記の角かっこを含む角かっこ内のすべてを削除することができますが、この物を通常のPOCOに逆シリアル化するために必要なすべての情報を提供します。

私がここで概説したすべてのことから、私はこのルートを進むことを本当にお勧めしませんが、フローに進む以外に選択肢がない場合もあり、タスクのより優れたものの改善が唯一の効果的な武器になる場合があります持ってる。

要約すると:

標準とパターンと慣行は、ある理由で存在します。その理由は、製品が最終的に交換されるまで、初期開発からALMサイクルにまで及びます。

私は何年にもわたって、彼らが取り組む必要のあるチケットのリストを完成する以外にこの全体像を見ることができないほど多くのDBA(およびその問題に関しては開発者)に遭遇しました。

チームメンバーは、他の誰よりも大きく、より良いことをする方法を決して探してはなりません。代わりに、常に共通点と常識を探している必要があります。

上記の他のポスターの1つが述べているように、シンプルさは常に最良のオプションです、何を忘れないでくださいKISSは、S ** tファンに当てはまるのは、KISS)かもしれません。

9
shawty

inner platform。 のように不審に聞こえます

潜在的な問題は次のとおりです。

  1. 必要な情報を単に取得するクエリではなく、最初にクエリする情報(メタクエリ)をデータベースにクエリするクエリを記述します。

  2. 行、列、意味のあるフィールド名やデータ型などのメタデータ機能をすでに提供しているデータベースの役割を覆します。

  3. データベースのインデックス作成機能とクエリ機能(JOINなど)を無効にします。

  4. マジックストリングeverywhereがあります。

これが発生する可能性のある極端な例については、次をお読みください システムを100%データ駆動型にすることはできますか?

オーソドックスなデータベースとアプリケーション設計を維持しながら、他のプログラマーが望むものを達成するためのより良い方法があります。 Automapper をチェックしてください。

18
Robert Harvey

私はこのような多くのパターンを見てきましたが、一般的には良いパターンです。表面的には複雑で扱いにくいように見えますが、非常によく似たデータアクセスブロックを繰り返さないようにするための良い方法です。

DBに返されたデータを連結することは悪い考えであり、3つのフィールドをクライアントで返して連結する必要があることにも同意します(まだ必要な場合)。ただし、最初に検討する必要がある質問の1つは、この連結形式がフォームの標準かどうかです。もしそうなら、それはDBでそれを行うことは理にかなっているように見えます(つまり、データはすでにその形式であるはずであり、DBの3つのフィールドとしてモデル化されていますが、データを単一のフィールドと考えるべきです、つまり、DBへのAPIが特定の形式のデータを返す必要がある場合、それを内部に格納する方法は無関係です)

1
gbjbaanb

すべてのプログラマーの心を恐れさせる3つの言葉'complex security scheme'。彼のスキームはこれまでに作成された中で最高のセキュリティ実装である可能性がありますが、それはホールでいっぱいになる可能性が高いです。私は常にそれがもう少しコードを書くことを意味するとしても、より単純である方がいいです。最高のコードにもバグがあり、政府のシステムで使用する場合、コードはできるだけ堅牢にする必要があります。

簡単に言えば、これは良い習慣ではなく、深刻なコードレビューに失敗する可能性があります。

0
Stevetech

彼のすべてのWebアプリケーションのドロップダウンリストが彼のMVC.NET C#コードとビューで同じオブジェクトを共有できるように、データベースから情報を「一般化」する方法。これには、使用されるテーブルに応じて異なるデータを含めることができます。

そのため、通常は問題の空間にマッピングするDBスキーマと、一般に問題の空間にマッピングするユーザーUIを使用して、通常は何にもマッピングしない2つの間にデータ転送メカニズムを作成します。うーん。

彼の正当な理由は、彼がさまざまなドロップダウンデータをすべて送信し、内容に関係なく、すべてを "LookupValue"という名前の同じC#オブジェクトにキャプチャできることです。

デザインはどこ?ドロップダウンリストへの入力には共通のクラスを使用しますが、SQLのカスタマイズ、リスト/コレクション自体の設定、リストのドロップダウンUI要素への関連付けなどのプロパティを備えたUIフォーム/コントロールです。同じように。

LookupValue

  • コメントは平凡で、明白で、不必要です
  • あなたの将来には、Open/CLosedの原則に違反することがたくさんあります。私の不思議な感覚は、これらすべての「このID」と「そのID」のパラメーター、およびコンストラクター全体でそれらが反復的であるとは、どういうわけか疑わしいと言います。もっと具体的になればいいのですが…コンストラクタごとにカスタマイズされたストアドプロシージャがあると思います。
  • 「Equateablitly」の実装は、クラスでは不完全です。継承された、オーバーライドされていないObject.Equals()IEquatable実装と同じ結果を返すとは確信していません。 MSDNから:「IEquatableを実装する場合は、Object.Equals(Object)の基本クラス実装もオーバーライドする必要があります」
  • LookupValueコレクションはありますか?そうでない場合、なぜIEquateableを実装するのですか?再びMSDNから:「IEquatableインターフェースは、ジェネリックコレクションオブジェクトによって使用されます...等価性をテストするときに...ジェネリックコレクションに格納される可能性のあるすべてのオブジェクトに対して実装する必要があります。」

Entity FrameworkをORMとして使用しており、彼のDALの彼のリポジトリクラスが、ストアドプロシージャからこのLookupValueオブジェクトにデータをダンプしていることを忘れていました。

したがって、彼は意図的にアプリケーションアーキテクチャを破壊しています。彼はそのための承認を持っていますか?意図的にORMから離れていますか?うーん。

これは、固有のデータバインディングをどれほどひどく壊しますか?そのためにどれだけ余分なコードを書きますか?

しかし、私にとってより深い問題は、彼がデータの一部を取得して、次のようなストアドプロシージャに連結していることです...

再利用性について議論するにはこれで終わりです。あなたはそれを「より深い問題」と呼ぶのは正しいです。

  • このため、まったく新しいストアドプロシージャを作成する必要がありました。そして、これらは特別なカスタマイズのために実質的に同一でした。これらの手順の1つは、すべての空白/ nullの数値がゼロであることを「決定」しました-合理的に聞こえますが、明らかにDBのみの問題です。それは次の要件サイクルを乗り切りませんでした!
  • その連結は受信側でどのように解析されますか?たぶんクラスじゃないですよね。私見、コーディング/意味が組み込まれている文字列は、意味が適切に表現されカプセル化されるように、それ自体がクラスである必要があります。

私の同僚は非常に複雑なセキュリティスキームを開発しました。これにより、地域、VISN、サイト、またはこれら3つを組み合わせて割り当てることができる権限レベルに基づいて、データへのアクセスを許可できます。それは素晴らしい計画であり、非常に賢いです。ただし、彼は、個人のユーザーIDに基づいてリージョン、VISN、およびサイトのリストを返すストアドプロシージャを持っています

そのため、彼は意図的にアプリケーションアーキテクチャを破壊しています。 (参照してください。その文を再利用しました!)。つまり、私はすべてがデータベースの組み込みのセキュリティとアクセス機能を利用していると思います。だから今、あなたはこれらを同期させておく必要があります!

結論として

このオブジェクトの再利用性は、関心の分離と...

再利用性は原則ではありません-それは結果です-優れたオブジェクト指向の設計とコーディングの。

彼は、「再利用可能」である単一のクラスを中心に、保守性とアンチデザインのタペストリーを織りました。これは明らかに、OO原則とガイドラインのバランスの取れた適用ではありません。

なぜ誰かがそのようなあいまいなことを犯すのでしょうか?心理学者は、他の人が理解していないものを作成することから、優越感を得ると言います。あなたは私にドキュメンテーションを見せることによってこれを反証するかもしれません。 何、ドキュメントなし?うーん。

0
radarbob