代码之家  ›  专栏  ›  技术社区  ›  James Black

由于循环引用,确定F#类型的顺序时出现问题

  •  13
  • James Black  · 技术社区  · 16 年前

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

    我现在需要一个函数,它允许我在给定任何模型类型的情况下查找id,因此我为一些其他函数创建了一个新类型。

    DAOMisc CityDAO 之前 道米斯克 ,这是不可能的。

    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 函数是我想要找到外键关系的id,这样我就可以在我的模型中设置值,然后让CRUD函数用所有正确的信息进行操作。所以, 需要州名的id,所以我会得到州名,把它放到 state 键入,然后调用此函数以获取该状态的id,因此my city insert还将包含外键的id。

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

    最终更新:

    在考虑了我的方法之后,我意识到它有致命的缺陷,所以我想出了一个不同的方法。

    我将这样做一个插入,我决定把这个想法放到每个实体类中,这可能是一个更好的想法。

    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 例如,对于selects。

    这是泛化的基类型插入:

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

    如果F#能做协方差/反方差就好了,所以我不得不绕过这个限制。

    4 回复  |  直到 16 年前
        1
  •  7
  •   Norman Ramsey    16 年前

    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#。希望这有帮助。

        2
  •  10
  •   Tomas Petricek    16 年前

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

    type CityDAO() = 
      inherit CommonDAO<CityType>(...)
      // we can use DAOMisc here
    
    and DAOMisc = 
      member internal self.FindIdByType item =  
        // we can use CityDAO here
    

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

    如果您首先为这两种类型定义接口,您可能会编写得更好—它们可能需要或不需要相互递归(取决于其中一种是否用于另一种的公共接口):

    type ICityDAO = 
      abstract Foo : // ...
    
    type IDAOMisc = 
      abstract Foo : // ...
    

    这有以下好处:

    • 在单个文件中定义所有相互递归的接口不会降低代码的可读性
    • 作为一个副作用,您将拥有更多可扩展的代码(多亏了接口)
        3
  •  5
  •   nachonachoman    12 年前

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

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

    关键是相互递归的类型是使用“and”操作符一起声明的(与两个单独的类型相反)

        4
  •  2
  •   Jason    16 年前

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

    这就是你说的“简单的方法是把这个函数放到每个DAO中…”。。。但是,我觉得这是错的?我不确定,因为你说过这个函数只会引用它前面的类型。我在这里提出的想法是,每个FindId函数只知道自己的类型。