WebSharper-如何将服务器上动态映射的策略模式对象公开给客户端?

时间:2019-01-09 12:15:52

标签: inheritance f# rpc websharper

我正在学习WebSharper,并且正在努力使某些逻辑在客户端上起作用。

我有一些具有继承层次结构的服务器端对象,我需要将它们公开给客户端。他们处理将页面的不同部分作为doc片段生成-但在客户端上。

2 > 100000
  
    

帖子已更新,有关旧版本,请参见history

  

在我的服务器端代码中,我有一张地图,将每个对象与给定名称相关联(如策略设计模式中一样):

[<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 模块将被编译为JS,如下所示:

ClientCode

到目前为止,我发现在[<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 would not work期间仅通过Rpc返回映射-要么返回了通用JS对象,要么我正在接收序列化错误。看起来这是WebSharper远程处理和客户端服务器通信的预期行为。

我知道我可以通过对地图中的afterRender实例进行硬编码来实现我的ClientModule.obtainObject,并且这样做确实有效,但是我需要避免这一部分。我正在开发的模块不必知道从A继承的类型的确切映射或实现(例如AB),也不必知道它们与什么名称相关联

我还需要使用什么其他方法将信息从服务器端对象映射传递到客户端?也许在我的代码中使用了C之类的东西?

更新1:我不一定需要实例化服务器上​​的对象。也许有一种方法可以将映射信息发送给客户端,并使其以某种方式进行实例化?

更新2:这是一个github repo,它简单地表示了我到目前为止的工作

更新3::一种替代方法是在服务器上保留一个映射,该映射将使用我的对象类型的名称而不是其实例(Quotation.Expr<A>)的实例。现在,如果我的客户代码看到Map<string, string>的完整类型名称是什么,是否可以完全从JavaScript调用该类型的默认构造函数?

1 个答案:

答案 0 :(得分:1)

这是另一种方法

在这种情况下,我创建了一个名为*sc = 'x';的字典,该字典根据文件和行号为每个类提供唯一的标识符。服务器和客户端版本略有不同。服务器版本使用类型名称作为密钥,而客户端使用文件名和行号作为密钥(Client.fs):

types

在服务器端,我将 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) 的{​​{1}}更改为_map。客户做同样的事情,但是相反。

字典Map<string,ClientCode.A>实际上是充当服务器和客户端在唯一名称和实际对象之间来回转换的字典。

(Site.fs):

Map<string, string>