13

我有一些扩展通用类型的类型,这些是我的模型。

然后,我为 CRUD 操作的每种模型类型提供了 DAO 类型。

我现在需要一个函数,它可以让我找到给定任何模型类型的 id,所以我为一些杂项函数创建了一个新类型。

问题是我不知道如何订购这些类型。目前我在 dao 之前有模型,但我不知何故需要DAOMiscbeforeCityDAOCityDAObefore DAOMisc,这是不可能的。

简单的方法是将这个函数放在每个 DAO 中,仅引用可以出现在它之前的类型,因此,在它State之前出现CityState具有外键关系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函数的目的是我想找到外键关系的 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#中,可以定义相互递归的类型,即可以将需要相互引用的两种类型定义在一起,它们会互相看到。写这个的语法是:

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

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

此语法的限制是这两种类型都需要在单个文件中声明,因此您不能使用典型的 C# 组织 1 类型每个文件。

正如 Norman 所指出的,这不是典型的功能设计,因此如果您以更功能的方式设计整个数据访问层,您可能可以避免这个问题。但是,我想说在 F# 中结合函数式和面向对象的风格并没有错,因此使用相互递归的类型可能是唯一的选择。

如果您首先为这两种类型定义接口,您可能可以更好地编写代码 - 这些可能需要也可能不需要相互递归(取决于一个是否用于另一个的公共接口):

type ICityDAO = 
  abstract Foo : // ...

type IDAOMisc = 
  abstract Foo : // ...

这有以下好处:

  • 在单个文件中定义所有相互递归的接口不会降低代码的可读性
  • 您可以稍后引用接口,因此不需要其他类型相互递归
  • 作为副作用,您将拥有更多可扩展的代码(感谢接口)
于 2010-05-18T02:22:39.547 回答
8

这个例子与我在函数式编程中所习惯的相差甚远。但是对于相互递归类型的排序问题,有一个标准的解决方案:使用类型参数,做两级类型。我将在 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#。希望这可以帮助。

于 2010-05-18T02:11:43.413 回答
5

F# 直接支持相互递归的类型。考虑以下鸡/蛋类型定义:

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

关键是使用“和”运算符一起声明相互递归的类型(而不是两个单独的类型)

于 2014-08-10T20:52:53.970 回答
2

如何消除 DAOMisc.FindIdByType,并在每个 DAO 类中用 FindId 替换它?FindId 只知道如何找到自己的类型。这将消除对基类和动态类型测试的需要,以及 DAOMisc 和所有其他 DAO 类之间的循环依赖。DAO 类型可以相互依赖,因此 CityDAO 可以调用 StateDAO.FindId。(如果需要,DAO 类型可以相互依赖。)

当你说“简单的方法是把这个函数放在每个 DAO 中......但是,这让我觉得这是错误的......”时,这就是你所说的吗?我不确定,因为您说该函数仅引用它之前的类型。我在这里提出的想法是每个 FindId 函数只知道自己的类型。

于 2010-05-18T03:31:20.633 回答