C# asp.net mvc web api使用OData修补程序进行部分更新
我正在使用HttpPatch部分更新对象。为了实现这一点,我使用了OData的Delta和Patch方法(这里提到:)。一切似乎都很好,但注意到mapper是区分大小写的;传递以下对象时,属性将获得更新的值:C# asp.net mvc web api使用OData修补程序进行部分更新,c#,api,asp.net-mvc-4,odata,C#,Api,Asp.net Mvc 4,Odata,我正在使用HttpPatch部分更新对象。为了实现这一点,我使用了OData的Delta和Patch方法(这里提到:)。一切似乎都很好,但注意到mapper是区分大小写的;传递以下对象时,属性将获得更新的值: { "Title" : "New title goes here", "ShortDescription" : "New text goes here" } 但当我传递具有小写或驼峰大小写属性的同一对象时,补丁不起作用-新值不会通过,因此反序列化和属性映射似乎存在问题,即:“sh
{
"Title" : "New title goes here",
"ShortDescription" : "New text goes here"
}
但当我传递具有小写或驼峰大小写属性的同一对象时,补丁不起作用-新值不会通过,因此反序列化和属性映射似乎存在问题,即:“shortDescription”到“shortDescription”
是否有使用补丁忽略区分大小写的配置部分?
供参考:
在输出时,我使用以下格式化程序具有camel case属性(以下是REST最佳实践):
//formatting
JsonSerializerSettings jss = new JsonSerializerSettings();
jss.ContractResolver = new CamelCasePropertyNamesContractResolver();
config.Formatters.JsonFormatter.SerializerSettings = jss;
//sample output
{
"title" : "First",
"shortDescription" : "First post!"
}
但是,我的模型类遵循C#/.NET格式约定:
public class Entry {
public string Title { get; set;}
public string ShortDescription { get; set;}
//rest of the code omitted
}
简短回答,否没有取消区分大小写的配置选项(据我所知) 详细回答:我今天遇到了和你一样的问题,我就是这样解决的
我发现它必须区分大小写,这让我非常恼火,因此我决定删除整个oData部分,因为它是一个巨大的库,我们正在滥用它……
这个实现的一个例子可以在我的github上找到 我决定实施我自己的补丁方法,因为这是我们实际上缺乏的肌肉。我创建了以下抽象类: 然后我让我的所有模型类继承自*MyModel*。注意我使用*let*的那一行,我稍后会解释。现在,您可以从控制器操作中删除Delta,只需将其重新设置为条目,就像put方法一样。例如 您仍然可以按照以前的方式使用修补方法:
var entry = dbContext.Entries.SingleOrDefault(p => p.ID == id);
newEntry.Patch(entry);
dbContext.SaveChanges();
现在,让我们回到台词上来
let attr = p.GetCustomAttribute(typeof(NotPatchableAttribute))
我发现这是一个安全风险,任何属性都可以通过补丁请求进行更新。例如,您现在可能希望修补程序可以更改an ID。我创建了一个自定义属性来装饰我的属性。不可修补属性:
public class NotPatchableAttribute : Attribute {}
public class User : MyModel
{
[NotPatchable]
public int ID { get; set; }
[NotPatchable]
public bool Deleted { get; set; }
public string FirstName { get; set; }
}
您可以像使用任何其他属性一样使用它:
public class NotPatchableAttribute : Attribute {}
public class User : MyModel
{
[NotPatchable]
public int ID { get; set; }
[NotPatchable]
public bool Deleted { get; set; }
public string FirstName { get; set; }
}
在此调用中,无法通过patch方法更改Deleted和ID属性
我希望这也能帮你解决这个问题。如果您有任何问题,请随时发表评论。
我添加了一张我在一个新的MVC5项目中检查道具的截图。如您所见,结果视图中填充了标题和简短描述。
使用继承CamelCasePropertyNamesContractResolver的自定义协定解析程序,并实现CreateContract方法,该方法查看delta的具体类型并获取实际的属性名,而不是使用来自json的属性名,可以非常轻松地完成此任务。摘要如下:
public class DeltaContractResolver : CamelCasePropertyNamesContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
// This class special cases the JsonContract for just the Delta<T> class. All other types should function
// as usual.
if (objectType.IsGenericType &&
objectType.GetGenericTypeDefinition() == typeof(Delta<>) &&
objectType.GetGenericArguments().Length == 1)
{
var contract = CreateDynamicContract(objectType);
contract.Properties.Clear();
var underlyingContract = CreateObjectContract(objectType.GetGenericArguments()[0]);
var underlyingProperties =
underlyingContract.CreatedType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in underlyingContract.Properties)
{
property.DeclaringType = objectType;
property.ValueProvider = new DynamicObjectValueProvider()
{
PropertyName = this.ResolveName(underlyingProperties, property.PropertyName),
};
contract.Properties.Add(property);
}
return contract;
}
return base.CreateContract(objectType);
}
private string ResolveName(PropertyInfo[] properties, string propertyName)
{
var prop = properties.SingleOrDefault(p => p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase));
if (prop != null)
{
return prop.Name;
}
return propertyName;
}
}
公共类DeltaContractResolver:CamelCasePropertyNamesContractResolver
{
受保护的重写JsonContract CreateContract(类型objectType)
{
//这个类是JsonContract的特例,只用于Delta类。所有其他类型都应该起作用
//像往常一样。
if(objectType.IsGenericType)&&
objectType.GetGenericTypeDefinition()==typeof(增量)&&
objectType.GetGenericArguments().Length==1)
{
var合同=CreateDynamicContract(objectType);
contract.Properties.Clear();
var underyingContract=CreateObjectContract(objectType.GetGenericArguments()[0]);
var基础属性=
underlineContract.CreatedType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach(underyingContract.Properties中的var属性)
{
property.DeclaringType=对象类型;
property.ValueProvider=新的DynamicObjectValueProvider()
{
PropertyName=this.ResolveName(underlyingProperties,property.PropertyName),
};
合同.属性.添加(属性);
}
退货合同;
}
返回base.CreateContract(objectType);
}
私有字符串ResolveName(PropertyInfo[]属性,字符串propertyName)
{
var prop=properties.SingleOrDefault(p=>p.Name.Equals(propertyName,StringComparison.OrdinalIgnoreCase));
如果(prop!=null)
{
返回道具名称;
}
返回propertyName;
}
}
Hi Rik,谢谢你的回答,我已经添加了代码,但是有一些不同之处(现在使用MVC 5),并且它不起作用:`var props=from p in this.GetType().GetProperties()让attr=p.GetCustomAttributes(typeof(NotPatchableAttribute),false)其中attr==null选择p;`但是,该查询不返回任何内容。@303哦。。我正在使用WebAPI2,让我在MVC5上快速测试它,我会给你回复的。。没有什么是无法解决的:)@303我刚刚在一个新的mvc项目中测试了它,它似乎可以工作。为了提高速度,我刚刚调整了编辑方法以使用补丁,它可以正常工作。我将把代码放在github上供您比较。仅供参考:我使用的是.Net 4.5,但出现以下错误:“System.Reflection.PropertyInfo”不包含“GetCustomAttribute”的定义,并且找不到接受“System.Reflection.PropertyInfo”类型第一个参数的扩展方法“GetCustomAttribute”(您缺少using指令或程序集引用吗?)我需要额外的引用吗?@303您需要使用System.Reflection;当然。我刚刚再次克隆了示例应用程序,为其创建了新的解决方案并重新安装(卸载并重新安装)下面的金块包装:实体框架,asp.NETMVC和asp.NETWeb优化框架。在这之后,我所有的参考资料都是好的,我是好的