如何获取IEdmEntityTypeReference.Key()以正确的顺序返回密钥?

时间:2020-04-15 17:27:40

标签: c# odata

我正在使用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值的方法。

1 个答案:

答案 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();
}