Pergunta

Eu tenho alguns tipos que estendem um tipo comum, e esses são meus modelos.

Eu tenho então tipos DAO para cada tipo de modelo para operações de CRUD.

Agora, tenho necessidade de uma função que me permitirá encontrar um ID com qualquer tipo de modelo, por isso criei um novo tipo para algumas funções diversas.

O problema é que não sei como pedir esses tipos. Atualmente tenho modelos antes do DAO, mas de alguma forma preciso DAOMisc antes da CityDAO e CityDAO antes da DAOMisc, o que não é possível.

A abordagem simples seria colocar essa função em cada DAO, referindo -se apenas aos tipos que podem vir antes, então, então, State vem antes City Como State tem um relacionamento chave estrangeiro com City, então a função diversa seria muito curta. Mas isso apenas me parece errado, então não tenho certeza de como melhor abordar isso.

Aqui está o meu tipo diverso, onde BaseType é um tipo comum para todos os meus modelos.

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

Aqui está um tipo DAO. CommonDAO Na verdade, tem o código para as operações do CRUD, mas isso não é importante aqui.

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
    )

Este é o meu tipo de modelo:

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

O propósito para isso FindIdByType A função é que eu quero encontrar o ID para um relacionamento chave estrangeiro, para que eu possa definir o valor no meu modelo e, em seguida, fazer com que as funções CRUD executem as operações com todas as informações corretas. Então, City Precisa do ID para o nome do estado, para que eu recebesse o nome do estado, colocasse -o no state Digite, então chame essa função para obter o ID para esse estado, para que minha inserção da cidade também inclua o ID da chave estrangeira.

Essa parece ser a melhor abordagem, de uma maneira muito genérica de lidar com inserções, que é o problema atual que estou tentando resolver.

ATUALIZAR:

Preciso pesquisar e ver se posso de alguma forma injetar o método FindIdbyType no Commondao depois que todos os outros DAOs foram definidos, quase como se fosse um fechamento. Se fosse Java, eu usaria a AOP para obter a funcionalidade que estou procurando, sem certeza de como fazer isso em F#.

Atualização final:

Depois de pensar na minha abordagem, percebi que era fatalmente falha, então criei uma abordagem diferente.

Foi assim que farei uma inserção e decidi colocar essa idéia em cada classe de entidade, o que provavelmente é uma ideia melhor.

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

Eu não comecei a usar o fklist No entanto, mas é int list e eu sei qual nome de coluna combina com cada um, então eu só preciso fazer inner join Para selecionar, por exemplo.

Esta é a inserção do tipo base que é generalizada:

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

Seria bom se F# pudesse fazer co/contra-variação, então eu tive que contornar essa limitação.

Foi útil?

Solução

Este exemplo está muito longe do que estou acostumado na programação funcional. Mas, para o problema de solicitar tipos mutuamente recursivos, existe uma solução padrão: use parâmetros de tipo e faça tipos de dois níveis. Vou dar um exemplo simples na OCAML, um idioma relacionado. Não sei como traduzir o exemplo simples nas funções do tipo assustador que você está usando.

Aqui está o que não funciona:

type misc = State of string
          | City  of city

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

Veja como você o conserta com tipos de dois níveis:

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

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

Este exemplo é OCAML, mas talvez você possa generalizado para f#. Espero que isto ajude.

Outras dicas

Em f#, é possível definir Tipos mutuamente recursivos, isto é, você pode definir os dois tipos que precisam se referir juntos e eles se verão. A sintaxe para escrever isso é:

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

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

A limitação dessa sintaxe é que os dois tipos precisam ser declarados em um único arquivo, para que você não possa usar o tipo típico de C# organização 1 por 1 arquivo.

Como Norman aponta, este não é um design funcional típico; portanto, se você projetou toda a camada de acesso a dados de uma maneira mais funcional, provavelmente poderá evitar esse problema. No entanto, eu diria que não há nada de errado em combinar o estilo funcional e orientado a objetos em F#, portanto, usar tipos mutuamente recursivos pode ser a única opção.

Você provavelmente pode escrever o código mais bem se definir interfaces para os dois tipos - elas podem ou não precisar ser mutuamente recursivas (dependendo se um é usado na interface pública do outro):

type ICityDAO = 
  abstract Foo : // ...

type IDAOMisc = 
  abstract Foo : // ...

Isso tem os seguintes benefícios:

  • Definir todas as interfaces mutuamente recursivas em um único arquivo não torna o código menos legível
  • Mais tarde, você pode se referir às interfaces, para que nenhum outro tipo precisa ser mutuamente recursivo
  • Como efeito colateral, você terá um código mais extensível (graças às interfaces)

F# suporta diretamente tipos mutuamente recursivos. Considere a seguinte definição do tipo de galinha/ovo:

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

O ponto é que os tipos mutuamente recursivos são declarados juntos usando o operador 'e' (em oposição a dois tipos separados)

Que tal eliminar Daomisc.findidbytype e substituí -lo por um findid dentro de cada classe Dao? Findid saberia apenas como encontrar seu próprio tipo. Isso eliminaria a necessidade da classe base e do teste de tipo dinâmico, e a dependência circular entre Daomisc e todas as outras classes DAO. Os tipos de DAO podem depender de um deles, para que o Citydao possa ligar para o Stateao.findid. (Os tipos de DAO podem depender mutuamente um do outro, se necessário.)

É disso que você estava falando quando disse: "A abordagem simples seria colocar essa função em cada DAO ... mas isso me parece errado ..."? Não tenho certeza porque você disse que a função se referiria apenas aos tipos que vêm antes dela. A ideia que estou apresentando aqui é que cada função FindID conhece apenas seu próprio tipo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top