2010-05-18 22 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