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

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

我现在需要一个函数,它允许我找到给定任何模型类型的 id,因此我为一些杂项函数创建了一个新类型。

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

简单的方法是将此函数放入每个 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 功能是我想找到外键关系的 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# 可以进行协变/逆变,那就太好了,所以我必须解决这个限制。

有帮助吗?

解决方案

这个例子是什么,我习惯于在函数式编程很远。但对于订购相互递归类型的问题,有一个标准的解决方案:使用类型参数,使两级类型。我给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# 组织每 1 个文件 1 个类型。

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

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

type ICityDAO = 
  abstract Foo : // ...

type IDAOMisc = 
  abstract Foo : // ...

这样做有以下好处:

  • 在单个文件中定义所有相互递归接口不会降低代码的可读性
  • 您可以稍后引用这些接口,因此其他类型不需要相互递归
  • 作为副作用,您将拥有更多可扩展的代码(感谢接口)

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

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

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

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

这就是你在谈论你的时候说,“简单的方法是把这个功能在每个DAO ...但是,这只是令我错了......”?我不知道,因为你说的那个功能只提及之前而来的类型。那我提出这里的想法是,每个FindId功能只知道自己的类型。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top