我正在使用OData,我对此不太熟悉。当OData在DataServiceContext类中将实体附加到Context时,他们将使用ODataResourceMetadataBuilder设置.Identity
属性。
ODataResourceMetadataBuilder entityMetadataBuilder = this.GetEntityMetadataBuilderInternal(descriptor);
descriptor.EditLink = entityMetadataBuilder.GetEditLink();
descriptor.Identity = entityMetadataBuilder.GetId();
这经历了一系列从ConventionalODataEntityMetadataBuilder.GetId()开始的OData类,最终到达了在OData的DataServiceUrlKeyDelimiter中创建CompositeKey的位置。
internal void AppendKeyExpression(IEdmStructuredValue entity, StringBuilder builder)
{
Debug.Assert(entity != null, "entity != null");
Debug.Assert(builder != null, "builder != null");
IEdmEntityTypeReference edmEntityTypeReference = entity.Type as IEdmEntityTypeReference;
if (edmEntityTypeReference == null || !edmEntityTypeReference.Key().Any())
{
throw Error.Argument(ErrorStrings.Content_EntityWithoutKey, "entity");
}
// Problem occurs here - edmEntityTypeReference.Key() has the keys in the wrong order.
this.AppendKeyExpression(edmEntityTypeReference.Key().ToList(), p => p.Name, p => GetPropertyValue(entity.FindPropertyValue(p.Name), entity.Type), builder);
}
在我的课堂上使用键注释定义了键
public class MyClass
{
[Key, Column(Order = 0)]
public Guid CompositeKeyB { get; set; }
[Key, Column(Order = 1)]
public Guid CompositeKeyA { get; set; }
}
请注意,键按字母顺序向后
当上面的代码运行以设置.Identity
字段时,它将按字母顺序而不是我在数据注释中指定的顺序提供键。
这是一个问题,因为检索实体时,键的顺序正确,因此它具有不同的.Identity
属性,并且被视为与附加实例不同的实例。这意味着它不会使用新数据更新Attached实体,并且还会在Context中创建该实体的第二个副本。
是否有一种简单的方法可以解决此问题,还是我坚持编写自己的代码以使用反射以正确的顺序获取键?我目前看不到在IEdmStructuralProperty
返回的IEdmEntityTypeReference.Key()
属性中查找Order值的方法。
答案 0 :(得分:1)
之所以出现此问题,是因为在ClientEdmModel.GetOrCreateEdmTypeInternal
方法中,它是按名称对属性进行排序,然后将它们分类为键和非键属性。
// Problem is the .OrderBy in this line of code
foreach (PropertyInfo property in ClientTypeUtil.GetPropertiesOnType(type, /*declaredOnly*/edmBaseType != null).OrderBy(p => p.Name))
{
IEdmProperty edmProperty = this.CreateEdmProperty((EdmStructuredType)entityType, property);
loadedProperties.Add(edmProperty);
if (edmBaseType == null && keyProperties.Any(k => k.DeclaringType == type && k.Name == property.Name))
{
Debug.Assert(edmProperty.PropertyKind == EdmPropertyKind.Structural, "edmProperty.PropertyKind == EdmPropertyKind.Structural");
Debug.Assert(edmProperty.Type.TypeKind() == EdmTypeKind.Primitive || edmProperty.Type.TypeKind() == EdmTypeKind.Enum, "edmProperty.Type.TypeKind() == EdmTypeKind.Primitive || edmProperty.Type.TypeKind() == EdmTypeKind.Enum");
loadedKeyProperties.Add((IEdmStructuralProperty)edmProperty);
}
}
我们使用的解决方案是在之后将原始排序应用于复合键属性,尽管潜在的解决方案也可能是从foreach循环中删除.OrderBy(p => p.Name)
。我不确定这是否还会导致其他问题,因此,之后只对复合键重新应用键排序顺序似乎是最安全的。
if (loadedKeyProperties.Count > 1)
{
var orderedKeyPropertyNames = keyProperties.Select(k => k.Name).ToList();
loadedKeyProperties = loadedKeyProperties.OrderBy(k => orderedKeyPropertyNames.IndexOf(k.Name)).ToList();
}