Проблема, определяющая, как заказать F#, вводит из-за циклических ссылок

У меня есть некоторые типы, которые расширяют общий тип, и это мои модели.

У меня затем есть типы ДАО для каждого типа модели для операций CRUD.

У меня теперь есть потребность в функции, которая позволит мне находить идентификатор, учитывая любой тип модели, таким образом, я создал новый тип для некоторых разных функций.

Проблема состоит в том, что я не знаю, как заказать эти типы. В настоящее время у меня есть модели перед дао, но мне так или иначе нужно DAOMisc прежде CityDAO и CityDAO прежде DAOMisc, который не возможен.

Простой подход должен был бы поместить эту функцию в каждый ДАО, относясь только к типам, которые могут прибыть перед ним, таким образом, State прибывает прежде City как State имеет отношения внешнего ключа с City, таким образом, разная функция была бы очень коротка. Но, это просто кажется мне неправильно, таким образом, я не уверен, как лучше всего приблизиться к этому.

Вот мой разный тип, где 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

Вот один тип дао. 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 функция состоит в том, что я хочу найти идентификатор для отношений внешнего ключа, таким образом, я могу установить значение в своей модели и затем иметь функции CRUD, делают операции со всей корректной информацией. Так, City нуждается в идентификаторе для имени состояния, таким образом, я получил бы имя состояния, поместил бы его в state введите, затем вызовите эту функцию для получения идентификатора для того состояния, таким образом, моя городская вставка будет также включать идентификатор для внешнего ключа.

Это, кажется, лучший подход, очень универсальным способом обработать вставляет, который является текущей проблемой, которую я пытаюсь решить.

ОБНОВЛЕНИЕ:

Я должен исследовать и видеть, могу ли я так или иначе ввести метод 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# мог бы сделать co/contra-variance, таким образом, я должен был работать вокруг того ограничения.

13
задан James Black 20 May 2010 в 00:01
поделиться

3 ответа

Этот пример очень далек от того, к чему я привык в функциональном программировании. Но для проблемы упорядочивания взаимно рекурсивных типов есть стандартное решение: использовать параметры типа и делать двухуровневые типы. Я приведу простой пример на OCaml, родственном языке. Я не знаю, как перевести этот простой пример в страшные функции типов, которые вы используете.

Вот что не работает:

type misc = State of string
          | City  of city

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

Вот как это исправить с помощью двухуровневых типов:

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

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

Этот пример на OCaml, но, возможно, вы сможете обобщить его на F#. Надеюсь, это поможет.

7
ответ дан 1 December 2019 в 22:06
поделиться

Как насчет исключения DAOMisc.FindIdByType и замены его на FindId в каждом классе DAO? FindId будет знать только, как найти свой собственный тип. Это устранит необходимость в базовом классе и тесте динамического типа, а также в циклической зависимости между DAOMisc и всеми другими классами DAO. Типы DAO могут зависеть друг от друга, поэтому CityDAO может вызывать StateDAO.FindId. (Типы DAO могут взаимно зависеть друг от друга, если это необходимо.)

Это то, о чем вы говорили, когда сказали: «Простым подходом было бы поместить эту функцию в каждый DAO ... Но это просто поражает. я как не прав ... "? Я не уверен, потому что вы сказали, что функция будет относиться только к предшествующим типам. Идея, которую я представляю здесь, заключается в том, что каждая функция FindId знает только свой собственный тип.

2
ответ дан 1 December 2019 в 22:06
поделиться

В F # можно определить взаимно рекурсивных типов , то есть вы можете определить два типа, которые должны ссылаться друг на друга вместе, и они будут видеть друг друга . Синтаксис для написания этого:

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

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

Ограничение этого синтаксиса состоит в том, что оба типа должны быть объявлены в одном файле, поэтому вы не можете использовать типичную организацию C # 1 тип на 1 файл.

Как указывает Норман, это не типичный функциональный дизайн, поэтому, если вы спроектировали весь уровень доступа к данным более функциональным образом, вы, вероятно, могли бы избежать этой проблемы. Однако я бы сказал, что нет ничего плохого в сочетании функционального и объектно-ориентированного стиля в F #, поэтому использование взаимно рекурсивных типов может быть единственным вариантом.

Вы, вероятно, сможете написать код более красиво, если сначала определите интерфейсы для двух типов - они могут или не должны быть взаимно рекурсивными (в зависимости от того, используется ли один в общедоступном интерфейсе другого):

type ICityDAO = 
  abstract Foo : // ...

type IDAOMisc = 
  abstract Foo : // ...

Это дает следующие преимущества:

  • Определение всех взаимно рекурсивных интерфейсов в одном файле не делает код менее читаемым
  • Вы можете позже ссылаться на интерфейсы, поэтому никакие другие типы не должны быть взаимно рекурсивными
  • Как побочный эффект, у вас будет больше расширяемого кода (благодаря интерфейсам)
10
ответ дан 1 December 2019 в 22:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: