WebSharper-如何将服务器上动态映射的策略模式对象公开给客户端? [英] WebSharper - How to expose dynamically mapped strategy-pattern objects on the server to the client?

查看:234
本文介绍了WebSharper-如何将服务器上动态映射的策略模式对象公开给客户端?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am at the process of learning WebSharper, and I am struggling with making some of my logic work on the client-side.

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

I have a few server-side objects with inheritance hierarchy, which I need to expose to the client-side. They deal with generating different parts of the page as doc fragments -- but on the client.

[<JavaScriptExport>]
type [<AbstractClass>] A() = 
    abstract member Doc: Map<string, A> -> Doc
    ...

[<JavaScriptExport>]
type [<AbstractClass>] B() =
    inherit A()

[<JavaScriptExport>]
type C() =
    inherit B()

帖子已更新,有关旧版本,请参见历史记录.

The post is updated, see history for the older version.

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

In my server-side code I have a map associating each object with a given name (as in the strategy design pattern):

let mutable objectMap : Map<string, A> = Map.empty

在某些时候,地图上充满了数据.这种情况仅在应用程序初始化阶段发生一次,但这是服务器端后端逻辑的结果.

At some point, the map gets filled with data. This happens once only in the application initialization phase, but is a result of the server-side backend logic.

现在,我只需要在客户端使用这些对象,就像下面过度简化的代码段中一样:

Now, I need to use those objects on the client-side only, like in the overly-simplified snippet below:

[<JavaScriptExport>]
type C() =
   inherit B() // or inherit A()
   override this.Doc map =
       div [] [ map.["innerDoc"].Doc(map)

在服务器端,我将拥有:

On the server-side end I would have:

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,如下所示:

The ClientCode module would be something that gets compiled to JS and would look like this:

[<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远程处理和客户端服务器通信的预期行为.

So far I've found out that simply returning the map via an Rpc during the afterRender would not work -- either generic JS objects are being returned, or I am receiving a serialization error. Looks like this is the expected behavior for the WebSharper remoting and clinet-server communication.

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

I know I could just implement my ClientModule.obtainObject by hardCoding the A instances inside my map and it does work if I do so, but I need to avoid that part. The module I am developing does not have to know the exact mapping or implementation of the types inheriting from A (like B and C for example), nor what names they have been associated with.

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

What other approaches I need to use to pass the information from the server-side object map to the client? Maybe use something like Quotation.Expr<A> in my code?

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

Update 1: I do not necessarily need to instantiate the objects on the server. Maybe there is a way to send the mapping information to the client and let it do the instantiation somehow?

更新2:这是一个 github存储库,具有简单的表示形式到目前为止我已经完成的工作

Update 2: Here is a github repo with a simple representation of what I have got working so far

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

Update 3: An alternative approach would be to keep on the server a mappping that would use the name of my object type instead of an instance of it (Map<string, string>). Now if my client code sees ClientAode.C of whatever the full type name is, is it possible to invoke the default constructor of that type entirely from JavaScript?

推荐答案

这是另一种方法

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

In this case I create a dictionary called types that gives each class a unique identifier based on the file and line number. The server and client versions are slightly different. The server version uses the type name as the key while the client uses the file & line number as a key (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>.客户做同样的事情,但是相反.

On the server side I changed the _map that was Map<string,ClientCode.A> to Map<string, string>. The client does the same thing but in reverse.

字典types实际上是充当服务器和客户端在唯一名称和实际对象之间来回转换的字典.

The dictionary types acts literally as a dictionary for both the server and the client to translate back and forth between unique name and actual object.

(Site.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
        )

这篇关于WebSharper-如何将服务器上动态映射的策略模式对象公开给客户端?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆