Question

J'ai quelques types qui se prolongent un type commun, et ce sont mes modèles.

J'ai alors les types de DAO pour chaque type de modèle pour les opérations CRUD.

J'ai maintenant besoin d'une fonction qui me permettra de trouver un identifiant donné tout type de modèle, donc je créé un nouveau type pour certaines fonctions diverses.

Le problème est que je ne sais pas comment commander ces types. À l'heure actuelle, j'ai modèles avant dao, mais je dois en quelque sorte DAOMisc avant CityDAO et CityDAO avant DAOMisc, ce qui est impossible.

L'approche simple serait de mettre cette fonction dans chaque DAO, se référant aux seuls types qui peuvent venir avant, donc, State vient avant City que State a une relation de clé étrangère avec City, de sorte que la fonction divers serait très court. Mais, ce me paraît mal, donc je ne suis pas certain comment mieux aborder ce sujet.

Voici mon type divers, où BaseType est un type commun pour tous mes modèles.

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

Voici un type dao. CommonDAO a en fait le code pour les opérations CRUD, mais qui n'a pas d'importance ici.

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
    )

Ceci est mon type de modèle:

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);]

Le but de cette fonction est FindIdByType que je veux trouver l'identifiant une relation clé étrangère, donc je peux définir la valeur dans mon modèle et les fonctions CRUD font les opérations avec toutes les informations correctes. Alors, City a besoin de l'identifiant le nom de l'Etat, donc j'obtenir le nom de l'État, le mettre dans le type de state, puis appelez cette fonction pour obtenir l'identifiant de cet état, donc mon insert de ville comprendra également l'ID de l'étranger clé.

Cela semble être la meilleure approche, d'une façon très générique pour les inserts de poignée, ce qui est le problème actuel que je suis en train de résoudre.

Mise à jour:

Je dois faire des recherches et voir si je peux en quelque sorte inject ai défini la méthode de FindIdByType dans le CommonDAO après que tous les autres OTI, presque comme si elle est une fermeture. Si tel était Java j'utilise AOP pour obtenir la fonctionnalité Je cherche, pas sûr comment faire cela en F #.

Mise à jour finale:

Après avoir réfléchi à mon approche, je compris qu'il était vouée à l'échec, alors je suis venu avec une approche différente.

Voici comment je vais faire un insert, et je décidé de mettre cette idée dans chaque classe d'entité, ce qui est probablement une meilleure idée.

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

Je n'ai pas commencé à utiliser le fklist encore, mais il est int list et je sais quel nom de colonne va de pair avec chacun, donc je juste besoin de faire inner join pour sélectionner, par exemple.

Ceci est l'insert de type de base qui est généralisée:

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

Ce serait bien si F # pourrait faire co / contre-variance, donc je devais travailler autour de cette limitation.

Était-ce utile?

La solution

Cet exemple est très loin de ce que je suis habitué à la programmation fonctionnelle. Mais pour le problème de la commande types mutuellement récursifs, il existe une solution standard: paramètres de type d'utilisation et de faire des types à deux niveaux. Je vais vous donner un exemple simple en OCaml, une langue apparentée. Je ne sais pas comment traduire l'exemple simple dans les fonctions de type effrayant que vous utilisez.

Voici ce que ne fonctionne pas:

type misc = State of string
          | City  of city

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

Voici comment vous le fixer avec les types à deux niveaux:

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

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

Cet exemple est OCaml, mais vous pouvez peut-être généralisé à F #. J'espère que cela t'aides.

Autres conseils

En F #, il est possible de définir types mutuellement récursifs , qui est, vous pouvez définir les deux types qui doivent se référencer mutuellement ensemble et ils le verront. La syntaxe pour écrire c'est:

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

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

La limitation de cette syntaxe est que les deux types doivent être déclarés dans un seul fichier, vous ne pouvez pas utiliser le type C # 1 fichier organisation type par 1.

Comme Norman souligne, ce n'est pas un design fonctionnel typique, donc si vous avez conçu toute la couche d'accès aux données d'une manière plus fonctionnelle, vous pouvez probablement éviter ce problème. Cependant, je dirais qu'il n'y a rien de mal avec la combinaison de style fonctionnel et orienté objet en F #, donc en utilisant des types mutuellement récursifs peut être la seule option.

Vous pouvez probablement écrire le code plus bien si vous devez d'abord définir des interfaces pour les deux types - ceux-ci peuvent ou non doivent être mutuellement récursive (selon que l'on utilise dans l'interface publique de l'autre):

type ICityDAO = 
  abstract Foo : // ...

type IDAOMisc = 
  abstract Foo : // ...

présente les avantages suivants:

  • Définir toutes les interfaces mutuellement récursifs dans un seul fichier ne fait pas le code moins lisible
  • Vous pouvez ensuite se référer aux interfaces, donc pas d'autres types doivent être mutuellement récursive
  • En effet secondaire, vous aurez un code plus extensible (grâce à des interfaces)

F # prend directement en charge les types mutuellement récursifs. Pensez à la définition de type poulet / oeuf suivant:

type Chicken =
   | Eggs of Egg list
and Egg =
   | Chickens of Chicken list

Le fait est que les types mutuellement récursifs sont déclarés en même temps en utilisant le « et » opérateur (par opposition à deux types distincts)

Que diriez-vous d'éliminer DAOMisc.FindIdByType, et le remplacer par un FindId dans chaque classe de DAO? FindId ne ferait savoir comment trouver son propre type. Cela permettrait d'éliminer la nécessité de la classe de base et un test de type dynamique, et la dépendance circulaire entre DAOMisc et toutes les autres classes DAO. types de DAO peuvent dépendre d'un autre, de sorte CityDAO peut appeler StateDAO.FindId. (Types de DAO pourraient dépendre mutuellement, le cas échéant.)

Est-ce que vous parlez quand vous avez dit: « L'approche simple serait de mettre cette fonction dans chaque DAO ... Mais, ce me frappe aussi mal ... »? Je ne sais pas parce que vous avez dit que la fonction se réfère uniquement aux types qui lui sont soumises. L'idée que je présente ici est que chaque fonction FindId ne connaît que son propre type.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top