[英]WCF - how to expose strongly typed objects to clients from a base type (without client casting?)
[英]WebSharper - How to expose dynamically mapped strategy-pattern objects on the server to the client?
我正在學習 WebSharper,我正在努力使我的一些邏輯在客戶端工作。
我有一些具有繼承層次結構的服務器端對象,我需要將它們公開給客戶端。 他們處理將頁面的不同部分生成為文檔片段——但在客戶端上。
[<JavaScriptExport>]
type [<AbstractClass>] A() =
abstract member Doc: Map<string, A> -> Doc
...
[<JavaScriptExport>]
type [<AbstractClass>] B() =
inherit A()
[<JavaScriptExport>]
type C() =
inherit B()
帖子已更新,請參閱舊版本的歷史記錄。
在我的服務器端代碼中,我有一個將每個對象與給定名稱相關聯的映射(如在策略設計模式中):
let mutable objectMap : Map<string, A> = Map.empty
在某些時候,地圖會充滿數據。 這僅在應用程序初始化階段發生一次,但它是服務器端后端邏輯的結果。
現在,我只需要在客戶端使用這些對象,就像下面過於簡化的代碼段一樣:
[<JavaScriptExport>]
type C() =
inherit B() // or inherit A()
override this.Doc map =
div [] [ map.["innerDoc"].Doc(map)
在服務器端,我會有:
module ServerSide =
let Main ctx endpoint ... =
// this is the mapping that is being generated on the server, derived somehow from the `objectMap ` above:
let serverMap : Map<string, something> = ...
let document =
[
div [ on.afterRender(fun _ -> ClientCode.fromServerMap(serverMap));][client <@ ClientCode.getDoc("someDoc") @>]
] |> Doc.Concat
Page.Content document
ClientCode
模塊會被編譯成 JS,看起來像這樣:
[<JavaScript>]
moduel ClientCode =
let _map : Var<Map<string, A>> = Var.Create <| Map.empty
let fromServerMap (serverMap : something) =
let clientMap : Map<string, A> = // TODO: get the information from server map
_map.Set clientMap
let getDoc (docName : string) =
_map.View.Map(fun m -> m.[docName].Doc(m))
|> Doc.EmbedView
到目前為止,我發現在afterRender
期間通過Rpc
簡單地返回地圖是行不通的——要么是返回了通用 JS 對象,要么是我收到了序列化錯誤。 看起來這是 WebSharper 遠程處理和客戶端服務器通信的預期行為。
我知道我可以通過在我的地圖中硬編碼A
實例來實現我的ClientModule.obtainObject
並且如果我這樣做它確實可以工作,但我需要避免那部分。 我正在開發的模塊不必知道從A
繼承的類型(例如B
和C
)的確切映射或實現,也不必知道它們與哪些名稱相關聯。
我需要使用哪些其他方法將信息從服務器端對象映射傳遞到客戶端? 也許在我的代碼中使用Quotation.Expr<A>
類的東西?
更新 1:我不一定需要實例化服務器上的對象。 也許有一種方法可以將映射信息發送到客戶端並讓它以某種方式進行實例化?
更新 2:這是一個github 存儲庫,其中簡單表示了我目前所做的工作
更新 3:另一種方法是在服務器上保留一個映射,該映射將使用我的對象類型的名稱而不是它的實例( Map<string, string>
)。 現在,如果我的客戶端代碼看到ClientAode.C
的完整類型名稱是什么,是否可以完全從 JavaScript 調用該類型的默認構造函數?
這是另一種看法
在這種情況下,我創建了一個名為types
的字典,它根據文件和行號為每個類提供一個唯一標識符。 服務器和客戶端版本略有不同。 服務器版本使用類型名稱作為鍵,而客戶端使用文件和行號作為鍵(Client.fs):
let types = new System.Collections.Generic.Dictionary<string, string * A>()
let registerType line (a:'a) =
if IsClient
then types.Add(line , (line, a :> A) )
else types.Add(typedefof<'a>.FullName, (line, a :> A) )
registerType (__SOURCE_FILE__ + __LINE__) <| C()
registerType (__SOURCE_FILE__ + __LINE__) <| D()
let fixType v =
match types.TryGetValue v with
| false, _ -> C() :> A
| true , (line, a) -> a
let fixMap (m:Map<string, string>) =
m |> Seq.map (fun kvp -> kvp.Key, fixType kvp.Value) |> Map
[<JavaScript>]
module Client =
let getDoc (m:Map<string, string>) (docName : string) =
let m = ClientCode.fixMap m
m.[docName].Doc(m)
在服務器端,我將Map<string,ClientCode.A>
的_map
更改為Map<string, string>
。 客戶端做同樣的事情,但相反。
字典types
實際上充當服務器和客戶端的字典,以便在唯一名稱和實際對象之間來回轉換。
(站點.fs):
[< JavaScript false >]
module Site =
open WebSharper.UI.Html
let HomePage _map ctx =
Templating.Main ctx EndPoint.Home "Home" [
Doc.ClientSide <@ Client.getDoc _map "C" @>
]
let mutable _map : Map<string, string> = Map.empty
let addMapping<'T> name =
match ClientCode.types.TryGetValue (typedefof<'T>.FullName) with
| false,_ -> printfn "Could not map %s to type %s. It is not registered" name (typedefof<'T>.FullName)
| true ,(line, a) ->
_map <- _map |> Map.add name line
addMapping<ClientCode.C> "C"
addMapping<ClientCode.D> "D"
[<Website>]
let Main =
Application.MultiPage (fun ctx endpoint ->
match endpoint with
| EndPoint.Home -> HomePage _map ctx
)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.