Data Service - 8. ASP.NET AJAX Sys.Data

[ 2009-04-29 12:01:45 | 作者: yuhen ]
字号: | |
ASP.NET AJAX 4.0 新增了对 ADO.NET Data Service 的支持,我们可以用 JavaScript 直接完成对 .svc 的调用。

以下示例使用的是 ASP.NET AJAX 4.0 Preview 4。在使用前,别忘了添加 MicrosoftAjax JS 引用。
<script src="MicrosoftAjax.js" type="text/javascript"></script>
<script src="MicrosoftAjaxAdoNet.js" type="text/javascript"></script>

该库的核心类是 Sys.Data.AdoNetServiceProxy,我们的服务操作也是通过它来完成的。

1. Query

通过资源寻址路径(URI),我们可以查询所需的实体对象。默认情况下,Sys.Data 会自动使用 JSON 格式。
<script type="text/javascript">
    function dataServiceQuery()
    {
        var dataService = new Sys.Data.AdoNetServiceProxy("Test.svc");
        var users = dataService.query("/Users?$top=1", callbackSuccess, callbackFailed);
    }
    
    function callbackSuccess(result, context, operation)
    {
        alert(result[0].Name);
    }

    function callbackFailed(error, context, operation)
    {
        alert(error._message);
    }
</script>

我们还可以用 Sys.Data.AdoNetQueryBuilder 来创建包含查询选项的 Request URI,避免手工书写可能造成的错误。
<script type="text/javascript">
    function dataServiceQuery()
    {
        var dataService = new Sys.Data.AdoNetServiceProxy("Test.svc");

        var queryBuilder = new Sys.Data.AdoNetQueryBuilder("/Users");
        queryBuilder.set_skip(2);
        queryBuilder.set_top(1);

        var users = dataService.query(queryBuilder.toString(), callbackSuccess, callbackFailed);
    }

    ...
</script>

2. Insert

AdoNetServiceProxy.insert 用于新增实体对象,并且会自动处理服务器返回的新增实体信息(比如 Id)。
<script type="text/javascript">
    function dataServiceInsert()
    {
        var dataService = new Sys.Data.AdoNetServiceProxy("Test.svc");

        var user = { Name:"userx", Age:18 };
        dataService.insert(user, "/Users", callbackSuccess, callbackFailed);
    }

    function callbackSuccess(result, context, operation)
    {
        alert("Success!\n  User Id = " + result.Id);
    }

    function callbackFailed(error, context, operation)
    {
        alert(error._message);
    }
</script>

uploads/200904/29_120404_snap1.png


3. Update

AdoNetServiceProxy.update 更新的实体对象必须包含 Meatadata 信息,否则无法定位更新目标。

uploads/200904/29_120409_snap2.png

<script type="text/javascript">
    var user;
    var dataService = new Sys.Data.AdoNetServiceProxy("Test.svc");

    function dataServiceQuery()
    {
        dataService.query("/Users?$top=1", function(result, context, operation)
        {
            user = result[0];
            alert(user.Name);
        });
    }

    function dataServiceUpdate()
    {
        user.Age += 1;
        dataService.update(user, callbackSuccess, callbackFailed);
    }

    function callbackSuccess(result, context, operation)
    {
        alert("Success!");
    }

    function callbackFailed(error, context, operation)
    {
        alert(error._message);
    }
</script>

<input id="btnQuery" type="button" value="Query" onclick="javascript:dataServiceQuery()" />
<input id="btnUpdate" type="button" value="Update" onclick="javascript:dataServiceUpdate()" />

4. Remove

要删除实体,同样要求实体对象包含 Metadata 进行资源定位。
<script type="text/javascript">
    var user;
    var dataService = new Sys.Data.AdoNetServiceProxy("Test.svc");

    function dataServiceQuery()
    {
        dataService.query("/Users?$top=1", function(result, context, operation)
        {
            user = result[0];
            alert(user.Name);
        });
    }

    function dataServiceRemove()
    {
        dataService.remove(user, callbackSuccess, callbackFailed);
    }

    function callbackSuccess(result, context, operation)
    {
        alert("Success!");
    }

    function callbackFailed(error, context, operation)
    {
        alert(error._message);
    }
</script>

<input id="btnTest" type="button" value="Query" onclick="javascript:dataServiceQuery()" />
<input id="btnDelete" type="button" value="Delete" onclick="javascript:dataServiceRemove()" />

5. Batch

Sys.Data 同样提供了批量提交的能力。
<script type="text/javascript">
    function dataServiceBatch()
    {
        var dataService = new Sys.Data.AdoNetServiceProxy("Test.svc");
        var actionSequence = dataService.createActionSequence();

        actionSequence.addInsertAction({ Name: "userA", Age: 1 }, "/Users");
        actionSequence.addInsertAction({ Name: "userB", Age: 2 }, "/Users");
        actionSequence.addInsertAction({ Name: "userC", Age: 3 }, "/Users");

        actionSequence.execute(callbackSuccess, callbackFailed);
    }

    function callbackSuccess(result, context, operation)
    {
        alert("Success!");
    }

    function callbackFailed(error, context, operation)
    {
        alert(error._message);
    }
</script>

我们监听一下 ASP.NET Development Server,看看提交内容。
Receive: Return Code: 0x00000000
POST /Test.svc/$batch HTTP/1.1
Accept: */*
Accept-Language: zh-cn
Referer: http://localhost:2394/Default.aspx
Content-Type: multipart/mixed; boundary=batch_8a71-67bb-9993
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; ... CIBA)
Host: localhost:2394
Content-Length: 986
Connection: Keep-Alive
Cache-Control: no-cache

--batch_8a71-67bb-9993
Content-Type: multipart/mixed;boundary=changeset_0b42-6552-980f

--changeset_0b42-6552-980f
Content-Type: application/http
Content-Transfer-Encoding: binary

POST /Users HTTP/1.1
Content-ID: 0
Host: localhost:2394
Accept: application/json
Accept-Charset: utf-8
Content-Type: application/json;charset=utf-8

{"Name":"userA","Age":1}
--changeset_0b42-6552-980f
Content-Type: application/http
Content-Transfer-Encoding: binary

POST /Users HTTP/1.1
Content-ID: 1
Host: localhost:2394
Accept: application/json
Accept-Charset: utf-8
Content-Type: application/json;charset=utf-8

{"Name":"userB","Age":2}
--changeset_0b42-6552-980f
Content-Type: application/http
Content-Transfer-Encoding: binary

POST /Users HTTP/1.1
Content-ID: 2
Host: localhost:2394
Accept: application/json
Accept-Charset: utf-8
Content-Type: application/json;charset=utf-8

{"Name":"userC","Age":3}
--changeset_0b42-6552-980f--
--batch_8a71-67bb-9993--

Send: Return Code: 0x00000000
HTTP/1.1 202 Accepted
Server: ASP.NET Development Server/9.0.0.0
Date: Wed, 29 Apr 2009 03:53:32 GMT
X-AspNet-Version: 2.0.50727
Cache-Control: no-cache
Content-Type: multipart/mixed; boundary=batchresponse_28aa2225-b569-4de0-b34f-9052a3cbbe41
Content-Length: 2251
Connection: Close

Send: Return Code: 0x00000000
--batchresponse_28aa2225-b569-4de0-b34f-9052a3cbbe41
Content-Type: multipart/mixed; boundary=changesetresponse_e9b5f241-4472-421f-9650-aa34285295a6

--changesetresponse_e9b5f241-4472-421f-9650-aa34285295a6
Content-Type: application/http
Content-Transfer-Encoding: binary

HTTP/1.1 201 Created
Content-ID: 0
Content-Type: application/json;charset=utf-8
Cache-Control: no-cache
Location: http://localhost:2394/Test.svc/Users(57)
DataServiceVersion: 1.0;

{
    "d": {
        "__metadata": {
            "uri": "http://localhost:2394/Test.svc/Users(57)",
            "type": "TestModel.User"
        },
        "Id": 57,
        "Name": "userA",
        "Age": 1,
        "UserDetail": {
            "__deferred": {
                "uri": "http://localhost:2394/Test.svc/Users(57)/UserDetail"
            }
        },
        "Orders": {
            "__deferred": {
                "uri": "http://localhost:2394/Test.svc/Users(57)/Orders"
            }
        }
    }
}

--changesetresponse_e9b5f241-4472-421f-9650-aa34285295a6
Content-Type: application/http
Content-Transfer-Encoding: binary

HTTP/1.1 201 Created
Content-ID: 1
Content-Type: application/json;charset=utf-8
Cache-Control: no-cache
Location: http://localhost:2394/Test.svc/Users(58)
DataServiceVersion: 1.0;

{
    "d": {
        "__metadata": {
            "uri": "http://localhost:2394/Test.svc/Users(58)",
            "type": "TestModel.User"
        },
        "Id": 58,
        "Name": "userB",
        "Age": 2,
        "UserDetail": {
            "__deferred": {
                "uri": "http://localhost:2394/Test.svc/Users(58)/UserDetail"
            }
        },
        "Orders": {
            "__deferred": {
                "uri": "http://localhost:2394/Test.svc/Users(58)/Orders"
            }
        }
    }
}

--changesetresponse_e9b5f241-4472-421f-9650-aa34285295a6
Content-Type: application/http
Content-Transfer-Encoding: binary

HTTP/1.1 201 Created
Content-ID: 2
Content-Type: application/json;charset=utf-8
Cache-Control: no-cache
Location: http://localhost:2394/Test.svc/Users(59)
DataServiceVersion: 1.0;

{
    "d": {
        "__metadata": {
            "uri": "http://localhost:2394/Test.svc/Users(59)",
            "type": "TestModel.User"
        },
        "Id": 59,
        "Name": "userC",
        "Age": 3,
        "UserDetail": {
            "__deferred": {
                "uri": "http://localhost:2394/Test.svc/Users(59)/UserDetail"
            }
        },
        "Orders": {
            "__deferred": {
                "uri": "http://localhost:2394/Test.svc/Users(59)/Orders"
            }
        }
    }
}

--changesetresponse_e9b5f241-4472-421f-9650-aa34285295a6--
--batchresponse_28aa2225-b569-4de0-b34f-9052a3cbbe41--

6. Lazy Load

利用 AdoNetServiceProxy.fetchDeferredProperty() 我们可以载入延迟加载的导航属性。
<script type="text/javascript">
    function dataServiceQuery()
    {
        var dataService = new Sys.Data.AdoNetServiceProxy("Test.svc");

        dataService.query("/Users?$top=1", function(result, context, operation)
        {
            var user = result[0];

            dataService.fetchDeferredProperty(user, "UserDetail", callbackSuccess, callbackFailed);
        });
    }

    function callbackSuccess(result, context, operation)
    {
        alert("Success!\nUser: " + result.Name + "\nDetail Telephone: " + result.UserDetail.Telephone);
    }

    function callbackFailed(error, context, operation)
    {
        alert(error._message);
    }
</script>

uploads/200904/29_143409_snap1.png


当然,也可以载入 Many-to-Many。
<script type="text/javascript">
    function dataServiceQuery()
    {
        var dataService = new Sys.Data.AdoNetServiceProxy("Test.svc");

        dataService.query("/Users?$top=1", function(result, context, operation)
        {
            var user = result[0];

            dataService.fetchDeferredProperty(user, "Orders", callbackSuccess, callbackFailed);
        });
    }

    function callbackSuccess(result, context, operation)
    {
        var sb = new Sys.StringBuilder();
        sb.appendLine("Success!");
        sb.appendLine("User: " + result.Name);
        sb.appendLine("Orders: ");

        for (var i = 0; i < result.Orders.length; i++)
        {
            var order = result.Orders[i];

            sb.appendLine("  " + order.Title + ", " + order.Price);
        }

        alert(sb.toString());
    }

    function callbackFailed(error, context, operation)
    {
        alert(error._message);
    }
</script>

uploads/200904/29_143415_snap2.png


:

AdoNetServiceProxy.fetchData() 可用于执行非 GET 操作(比如 Service Operation),不过 MicrosoftAdoNet.js 中代码似乎没写全,导致该方法无法正常使用。
function Sys$Data$AdoNetServiceProxy$fetchData(operation, parameters, mergeOption, httpVerb, 
    succeededCallback, failedCallback, timeout, userContext)
{
    /// <summary locid="M:J#Sys.Data.AdoNetServiceProxy.fetchData" />
    /// <param name="operation"></param>
    /// <param name="parameters" type="Object" mayBeNull="true" optional="true"></param>
    /// <param name="mergeOption" type="Sys.Data.MergeOption" mayBeNull="true" optional="true"></param>
    /// <param name="httpVerb" type="String" mayBeNull="true" optional="true"></param>
    /// <param name="succeededCallback" type="Function" mayBeNull="true" optional="true"></param>
    /// <param name="failedCallback" type="Function" mayBeNull="true" optional="true"></param>
    /// <param name="timeout" type="Number" integer="true" mayBeNull="true" optional="true"></param>
    /// <param name="userContext" mayBeNull="true" optional="true"></param>
    /// <returns type="Sys.Net.WebRequest"></returns>

    var e = Function._validateParams(arguments, 
    [
        { name: "operation" },
        { name: "parameters", type: Object, mayBeNull: true, optional: true },
        { name: "mergeOption", type: Sys.Data.MergeOption, mayBeNull: true, optional: true },
        { name: "httpVerb", type: String, mayBeNull: true, optional: true },
        { name: "succeededCallback", type: Function, mayBeNull: true, optional: true },
        { name: "failedCallback", type: Function, mayBeNull: true, optional: true },
        { name: "timeout", type: Number, mayBeNull: true, integer: true, optional: true },
        { name: "userContext", mayBeNull: true, optional: true }
    ]);

    if (e) throw e;
    if (!this.get_serviceUri())
    {
        throw Error.invalidOperation(Sys.Data.AdoNetRes.requiredUri);
    }
    if (!operation)
    {
        throw Error.argumentNull("operation");
    }
    var request, oldTimeout = null;
    if (typeof (timeout) !== "undefined")
    {
        oldTimeout = this.get_timeout();
        this.set_timeout(timeout);
    }
    if (parameters)
    {
        var q = "";
        for (var name in parameters)
        {
            if (q)
            {
                q += "&";
            }
            q += encodeURIComponent(name) + "=" + encodeURIComponent(parameters[name]);
        }
        if (operation.indexOf("?") === -1)
        {
            operation += "?" + q;
        }
        else
        {
            operation += "&" + q;
        }
    }
    request = this.query(operation, succeededCallback, failedCallback, userContext);
    if (oldTimeout !== null)
    {
        this.set_timeout(oldTimeout);
    }
    return request;
}

-------- 分割线 ------------

我对 Microsoft ASP.NET AJAX 比较陌生,用起来有点别扭。不过作为 .NET 一个重要的客户端库,看样子还是得花点时间研究一下。
[最后修改由 yuhen, 于 2009-04-29 14:49:31]
评论Feed 评论Feed: http://www.rainsts.net/feed.asp?q=comment&id=813

这篇日志没有评论。

发表评论
表情图标
[smile] [confused] [cool] [cry]
[eek] [angry] [wink] [sweat]
[lol] [stun] [razz] [redface]
[rolleyes] [sad] [yes] [no]
[heart] [star] [music] [idea]
UBB代码
转换链接
表情图标
悄悄话
用户名:   密码:  
验证码 * 请输入验证码