Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/google-apps-script/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Wcf data services 数据服务<;T>;支架向上插入(插入或更换)_Wcf Data Services - Fatal编程技术网

Wcf data services 数据服务<;T>;支架向上插入(插入或更换)

Wcf data services 数据服务<;T>;支架向上插入(插入或更换),wcf-data-services,Wcf Data Services,据我所知,基于OData的服务支持“upsert”(即,插入一行,或者在已经存在具有该键的行时更新该行)的通常方式是通过一个PUT请求,该请求包括一个针对行和分区键的过滤器 http://myaccount.table.core.windows.net/mytable(PartitionKey='myPartitionKey', RowKey='myRowKey1') 据我所知,Azure表存储就是这样支持upsert的。但据我所知,如果您在自己的OData服务上尝试同样的方法,该服务是使用.

据我所知,基于OData的服务支持“upsert”(即,插入一行,或者在已经存在具有该键的行时更新该行)的通常方式是通过一个PUT请求,该请求包括一个针对行和分区键的过滤器

http://myaccount.table.core.windows.net/mytable(PartitionKey='myPartitionKey', RowKey='myRowKey1')
据我所知,Azure表存储就是这样支持upsert的。但据我所知,如果您在自己的OData服务上尝试同样的方法,该服务是使用.NET Framework的内置
数据服务实现的,那么只有在行已经存在的情况下,这才会成功。如果该行不存在,我将得到一个404错误

换句话说,这只适用于更新,不适用于插入


我怀疑upsert根本不受支持,但还没有找到一个明确的答案。有人能告诉我怎么做,或者确认我绝对不能吗?

您可以尝试创建自己的自定义查询提供程序(IDataServiceQueryProvider的一个实现)。如果用户请求的是一个不存在的对象,而当前http请求的方法被放入,则返回一个具有给定id的新对象。我认为内置更新提供程序应该能够从那里处理它并更新记录。否则,您可能还需要自己的更新提供程序

使用会使你的工作更容易。否则,您将不得不编写自己的linq提供程序,这对于集成测试来说似乎有些过分


是msdn上的一系列博客文章,介绍了如何构建自定义ds提供程序。

您可以尝试创建自己的自定义查询提供程序(IDataServiceQueryProvider的一个实现)。如果用户请求的是一个不存在的对象,而当前http请求的方法被放入,则返回一个具有给定id的新对象。我认为内置更新提供程序应该能够从那里处理它并更新记录。否则,您可能还需要自己的更新提供程序

使用会使你的工作更容易。否则,您将不得不编写自己的linq提供程序,这对于集成测试来说似乎有些过分


msdn上有一系列很好的博客文章,描述了如何构建定制ds提供商。

好的,我已经尝试了Jason Freitas的建议,结果太复杂,无法在评论中加以说明,因此我添加了一个答案

tl;dr:虽然解决方案围绕着
IDataServiceUpdateProvider
iUpdateTable
展开,但您可以这样做。(Jason建议,
IDataServiceQueryProvider
,这似乎没有帮助。)问题是,
DataService
并不是真正设计用来支持upsert的,它用于更新的接口也不是,所以尽管你可以让它工作,但解决方案是一种黑客行为(不是一种好的方式),我怀疑这可能会在将来引起问题

长版本:

我已经实现了
IUpdatable
,这是支持更新、插入和删除所必需的
IDataServiceQueryProvider
没有添加任何与更新支持相关的内容,因此
IUpdatable
是关键。我最初错误地认为,即使请求的项目不存在,安排
IUpdatable.GetResource
返回项目也会扰乱查询行为。但是当然,对
DataService
的查询不会经过
IUpdatable
,因此无论请求什么,都可以让该方法返回对象

要做到这一点令人惊讶地复杂,而且事实证明这还不够。代码如下:

public object GetResource(IQueryable query, string fullTypeName)
{
    var item = query.Cast<object>().SingleOrDefault();
    if (item == null && fullTypeName != null)
    {
        var ctor = Type.GetType(fullTypeName).GetConstructor(Type.EmptyTypes);
        if (ctor != null)
        {
            item = ctor.Invoke(null);
            PopulatePutStandin(query.Expression, item);
        }
    }

    return item;
}

private void PopulatePutStandin(Expression expression, object item)
{
    var call = expression as MethodCallExpression;
    if (call != null && call.Method.Name == "Where" && call.Method.DeclaringType == typeof(Queryable))
    {
        foreach (Expression arg in call.Arguments)
        {
            var ux = arg as UnaryExpression;
            if (ux != null)
            {
                var op = ux.Operand as LambdaExpression;
                if (op != null)
                {
                    var bx = op.Body as BinaryExpression;
                    if (bx != null && bx.Method.Name == "op_Equality")
                    {
                        var left = bx.Left as MemberExpression;
                        var right = bx.Right as ConstantExpression;
                        if (left != null && right != null)
                        {
                            var prop = left.Member as PropertyInfo;
                            if (prop != null)
                            {
                                prop.SetValue(item, right.Value);
                            }
                        }
                    }
                }
            }
            else
            {
                PopulatePutStandin(arg, item);
            }
        }
    }
}

这两者应该具有相同的效果。它恰好使用了前者,我的代码也依赖于此,但我还没有发现任何
DataService
承诺以任何特定形式提供查询的文档。因此,我们对
DataService
中实现细节的更改非常敏感。一个更健壮的实现可能需要处理这两种形式,尽管这感觉很麻烦——理论上,我们可以被问到很多种方法。目前还不清楚是否有一种完全通用且安全的方法来创建一个新对象,该对象与我们在这里可能收到的任何查询都匹配

然而,这是在测试中运行的代码,所以我们将在开发时检测这种问题,所以我想这是可以接受的

然而,尽管这使我们能够为看跌期权制定一个合适的目标,但结果证明这还不够。我们得到一个带有以下错误的
DataServiceException

自实体类型 “Mm.Web.Tests.Fakes.AzureTableStorage.FakeUserPermission”有一个或多个 如果必须为指定匹配HTTP头,则必须指定更多etag属性 删除/放置此类型的操作

DataService
的内部决定,如果PUT请求有意义,那么它必须包含一个ETag,因为除此之外,您如何确保您正在编辑要编辑的实体?这对于更新是有意义的,但对于插入显然没有意义。所以这不是一个好主意

我尝试在客户端包含一个etag:

var permission = new TableEntity(userId, claimId) { ETag = "*" };
await _myTable.ExecuteAsync(TableOperation.InsertOrReplace(permission));
然而,Azure表存储客户机显然足够聪明,知道ETAG对于upsert毫无意义。(如果你知道etag,你知道这绝对是一个更新,所以你不应该使用upsert。)所以它实际上不会传递etag

但是,您可以解决这个问题。除了实现
IUpdatable
,您还可以实现
IDataServiceUpdateProvider
,它源自
IUpdatable
,并添加一个成员:

public void SetConcurrencyValues(
    object resourceCookie,
    bool? checkForEquality,
    IEnumerable<KeyValuePair<string, object>> concurrencyValues)
{
}
public void设置并发值(
对象资源cookie,
布尔?检查质量,
IEnumerable并发值)
{
}
如果实现这个接口,基本上就是告诉
DataService
您希望自己处理etag。既然我很高兴没有ETag,这也没关系。仅提供此inte的空实现
var permission = new TableEntity(userId, claimId) { ETag = "*" };
await _myTable.ExecuteAsync(TableOperation.InsertOrReplace(permission));
public void SetConcurrencyValues(
    object resourceCookie,
    bool? checkForEquality,
    IEnumerable<KeyValuePair<string, object>> concurrencyValues)
{
}