13

共通の型を拡張するいくつかの型があり、これらは私のモデルです。

次に、CRUD 操作のモデル タイプごとに DAO タイプを用意します。

任意のモデル タイプから ID を検索できる関数が必要になったので、その他の関数用に新しいタイプを作成しました。

問題は、これらのタイプを注文する方法がわからないことです。現在、私は dao の前にモデルを持っていますが、どういうわけかDAOMiscbeforeCityDAOCityDAObeforeが必要ですDAOMiscが、これは不可能です。

簡単なアプローチは、この関数を各 DAO に配置し、その前に来ることができる型のみを参照することです。したがって、との外部キー関係があるasのState前に来るので、その他の関数は非常に短くなります。しかし、これは間違っていると思うので、これにどのようにアプローチするのが最善かはわかりません。CityStateCity

これが私のその他のタイプBaseTypeです。 はすべてのモデルに共通のタイプです。

type DAOMisc =
    member internal self.FindIdByType item = 
        match(item:BaseType) with
        | :? StateType as i -> 
            let a = (StateDAO()).Retrieve i
            a.Head.Id
        | :? CityType as i -> 
            let a = (CityDAO()).Retrieve i
            a.Head.Id
        | _ -> -1

ここに 1 つの dao タイプがあります。CommonDAO実際には CRUD 操作のコードがありますが、ここでは重要ではありません。

type CityDAO() =
    inherit CommonDAO<CityType>("city", ["name"; "state_id"], 
        (fun(reader) ->
            [
                while reader.Read() do
                    let s = new CityType()
                    s.Id <- reader.GetInt32 0
                    s.Name <- reader.GetString 1
                    s.StateName <- reader.GetString 3
            ]), list.Empty
    )

これは私のモデルタイプです:

type CityType() =
    inherit BaseType()
    let mutable name = ""
    let mutable stateName = ""
    member this.Name with get() = name and set restnameval=name <- restnameval
    member this.StateName with get() = stateName and set stateidval=stateName <- stateidval
    override this.ToSqlValuesList = [this.Name;]
    override this.ToFKValuesList = [StateType(Name=this.StateName);]

このFindIdByType関数の目的は、外部キー関係の ID を見つけることです。そのため、モデルに値を設定し、CRUD 関数にすべての正しい情報を使用して操作を実行させることができます。したがって、City州名の ID が必要なので、州名を取得してstate型に入れ、この関数を呼び出してその州の ID を取得します。したがって、都市の挿入には外部キーの ID も含まれます。

これは、私が解決しようとしている現在の問題である挿入を処理するための非常に一般的な方法で、最良のアプローチのようです。

アップデート:

他のすべての DAO が定義された後、FindIdByType メソッドを何らかの方法で CommonDAO に挿入できるかどうかを調査して確認する必要があります。これが Java の場合、AOP を使用して探している機能を取得しますが、F# でこれを行う方法は不明です。

最終更新:

自分のアプローチについて考えた後、致命的な欠陥があることに気付き、別のアプローチを考え出しました。

これが挿入を行う方法であり、このアイデアを各エンティティ クラスに入れることにしました。これはおそらくより良いアイデアです。

member self.Insert(user:CityType) =
    let fk1 = [(StateDAO().Retrieve ((user.ToFKValuesList.Head :?> StateType), list.Empty)).Head.Id]
    self.Insert (user, fk1)

fklist私はまだ使い始めていませんint listが、それぞれの列名がどの列名に対応するかを知っているのでinner join、たとえば選択に対して行う必要があります。

これは、一般化された基本タイプの挿入です。

member self.Insert(user:'a, fklist) =
    self.ExecNonQuery (self.BuildUserInsertQuery user)

F# で共分散/反分散を実行できるとよいので、その制限を回避する必要がありました。

4

4 に答える 4

10

F# では、相互に再帰的な型を定義できます。つまり、相互に参照する必要がある 2 つの型を定義すると、相互に認識されます。これを記述するための構文は次のとおりです。

type CityDAO() = 
  inherit CommonDAO<CityType>(...)
  // we can use DAOMisc here

and DAOMisc = 
  member internal self.FindIdByType item =  
    // we can use CityDAO here

この構文の制限は、両方の型を 1 つのファイルで宣言する必要があるため、1 つのファイルごとに典型的な C# 組織の 1 つの型を使用できないことです。

Norman が指摘するように、これは典型的な機能設計ではないため、データ アクセス レイヤー全体をより機能的な方法で設計すれば、おそらくこの問題を回避できます。ただし、F# で関数型とオブジェクト指向のスタイルを組み合わせても問題はないと思います。そのため、相互に再帰的な型を使用することが唯一の選択肢かもしれません。

最初に 2 つの型のインターフェイスを定義すると、コードをより適切に記述できる可能性があります。これらは相互に再帰的である必要がある場合とそうでない場合があります (一方が他方のパブリック インターフェイスで使用されているかどうかによって異なります)。

type ICityDAO = 
  abstract Foo : // ...

type IDAOMisc = 
  abstract Foo : // ...

これには次の利点があります。

  • 単一のファイルですべての相互再帰インターフェースを定義しても、コードが読みにくくなることはありません
  • 後でインターフェイスを参照できるため、他の型を相互に再帰的にする必要はありません
  • 副作用として、より拡張可能なコードが得られます (インターフェイスのおかげで)
于 2010-05-18T02:22:39.547 に答える
8

この例は、私が慣れ親しんでいる関数型プログラミングとはかけ離れています。しかし、相互に再帰的な型の順序付けの問題については、標準的な解決策があります。型パラメーターを使用し、2 レベルの型を作成します。関連言語である OCaml での簡単な例を示します。簡単な例を、あなたが使用している怖いタイプの関数に変換する方法がわかりません。

機能しないものは次のとおりです。

type misc = State of string
          | City  of city

type city = { zipcode : int; location : misc }

これを 2 レベル タイプで修正する方法は次のとおりです。

type 'm city' = { zipcode : int; location : 'm }

type misc = State of string
          | City of misc city'
type city = misc city'

この例は OCaml ですが、F# に一般化できるかもしれません。お役に立てれば。

于 2010-05-18T02:11:43.413 に答える
2

DAOMisc.FindIdByTypeを削除し、各DAOクラス内でFindIdに置き換えるのはどうですか?FindIdは、それ自体のタイプを見つける方法しか知りません。これにより、基本クラスと動的型テストの必要がなくなり、DAOMiscと他のすべてのDAOクラスとの間の循環依存関係がなくなります。DAOタイプは相互に依存する可能性があるため、CityDAOはStateDAO.FindIdを呼び出すことができます。(DAOタイプは、必要に応じて相互に依存する可能性があります。)

これは、「この関数を各DAOに配置するのが簡単なアプローチですが、これは間違っていると思います...」と言ったときに話していたものですか?関数はその前にある型のみを参照するとおっしゃっていたので、よくわかりません。ここで紹介しているのは、各FindId関数は独自の型のみを知っているということです。

于 2010-05-18T03:31:20.633 に答える