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

StackOverflow https://stackoverflow.com/questions/2854145

Вопрос

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

Затем у меня есть типы DAO для каждого типа модели для трудных операций.

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

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

Простой подход будет поставить эту функцию в каждом DAO, ссылаясь на только типы, которые могут прийти до этого, так, 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

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

Это было полезно?

Решение

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

Другие советы

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

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

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

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

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

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

type ICityDAO = 
  abstract Foo : // ...

type IDAOMisc = 
  abstract Foo : // ...

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

  • Определение всех взаимно рекурсивных интерфейсов в одном файле не делает код менее читаемым
  • Позже вы можете ссылаться на интерфейсы, поэтому другие типы не должны взаимно рекурсивными
  • В качестве побочного эффекта у вас будет более расширяемый код (благодаря интерфейсам)

F # напрямую поддерживает взаимно рекурсивные типы. Рассмотрим следующее определение типа куриного / яичка:

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

Дело в том, что взаимно рекурсивные типы объявлены вместе с использованием оператора «и» (в отличие от двух отдельных типов)

Как насчет устранения DAOMISC.Findidbytype и замена его на Fixid в каждом классе DAO? LASTID только знал, как найти свой тип. Это устранило бы необходимость в тесте базового класса и динамического типа, а также круговую зависимость между DAOMISC и всеми другими классами DAO. Типы дао могут зависеть друг от друга, поэтому CityDao может позвонить Statedao.findid. (Дао типа могут зависеть друг от друга взаимно, если нужно быть.)

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top