2010-05-18 47 views
12

我有一些扩展常见类型的类型,这些是我的模型。由于循环引用而决定如何订购F#类型的问题

然后,我为每个CRUD操作的模型类型都有DAO类型。

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

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

简单的办法是把每个DAO此功能,指的只是前,可以来的类型,所以,State到来之前CityStateCity一个外键关系,所以辅助功能会很短。但是,这只是我的错误,所以我不确定如何最好地解决这个问题。

这是我的杂项类型,其中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功能的目的是,我想找到一个外键关系的ID,这样我就可以在我的模型中设定的值然后让CRUD功能使用所有正确的信息进行操作。因此,City需要国家名称的ID,所以我会得到州名称,将其放入state类型,然后调用此函数来获取该州的ID,所以我的城市插入也将包含外部ID键。

这似乎是最好的方法,以一种非常通用的方式来处理插入,这是我正在尝试解决的当前问题。

UPDATE:

我需要研究,看看我能以某种方式注入FindIdByType方法进入CommonDAO所有其他的DAO已经确定之后,几乎就像是一个封闭。如果这是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 /禁忌变化,所以我必须解决这个限制。

+0

参见HTTP://计算器.com/questions/1378575/f-forward-type-declarations – Brian 2010-05-18 02:59:17

+0

@Brian - 直到我想到另外一个问题时,我并没有考虑过使用几种类型。对我来说这似乎是一个有缺陷的设计的破解,但我认为这是最好的方法。 – 2010-05-18 10:33:13

回答

7

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

+1

我需要反思一下你在这里展示的内容,看起来这可能是比我所做的更好的方法。 – 2010-05-18 10:31:35

+0

考虑到你写的内容后,我意识到你是正确的,因为我的方法是错误的。 – 2010-05-20 00:03:04

+0

我不得不考虑一些关于这个很好的解决方案的时刻:) – 2016-07-17 00:17:05

10

在F#中,可以定义相互递归的类型,也就是说,您可以定义需要彼此引用的两种类型,它们将彼此看到。写这个语法是:

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

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

这句法的限制是两个类型的需要在一个文件中声明,所以你不能每1个文件中使用典型的C#组织1分型。正如Norman指出的那样,这不是一个典型的功能设计,所以如果你设计了更多功能的整个数据访问层,你可能可以避免这个问题。但是,我认为在F#中将功能和面向对象的风格结合起来没有任何问题,因此使用相互递归的类型可能是唯一的选择。

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

type ICityDAO = 
    abstract Foo : // ... 

type IDAOMisc = 
    abstract Foo : // ... 

这有以下好处:

  • 定义所有相互递归接口在一个文件中不会使代码的可读性变差
  • 以后,您可以参考接口,所以没有其他类型的需要是相互递归
  • 作为一个副作用,你就会有更多的可扩展的代码(感谢接口)
+0

我可能会用一个界面去,但我对解决方案不满意,因为它似乎是kludgy。我认为我的设计有一些根本性的错误。我在关于这个问题的问题上添加了一个编辑。 – 2010-05-18 09:47:33

2

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

这就是你说的时候你说的,“简单的方法是把这个函数放在每个DAO中......但是,这只是打我是错的......“?我不确定,因为你说这个函数只会引用它之前的类型。我在这里介绍的想法是每个FindId函数只知道它自己的类型。

+0

我试图减少尽可能多的重复代码,并尽可能保持我的函数为通用函数,这就是为什么在每个dao类中重复函数会成为问题的原因。 – 2010-05-18 09:29:36

+1

你的想法是我去的 – 2010-05-20 00:02:24

3

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

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

的一点是,相互递归类型声明一起使用“和”操作员(而不是两个单独的类型)

+0

我相信我已经尝试过你的方法,并且当我写下我的问题时,我无法完成它的工作。 F#3.0可能已经使这成为可能,我在这一点上还没有尝试过。 – 2014-08-11 20:32:46