[英]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.