简体   繁体   English

OData 只读属性

[英]OData read-only property

I have a WebAPI 2.2 application with OData V4.我有一个带有 OData V4 的 WebAPI 2.2 应用程序。 Also I'm using EF 6.1.我也在使用 EF 6.1。

In one of my entities I have a calculated property:在我的一个实体中,我有一个计算属性:

public class Person {
  public string FirstName { get; set; }
  public string LastName { get; set; }
  // Calculated Property - No setter
  public string FullName { 
    get {
      return FirstName + " " + LastName;
    }
} 

In order to provide the calculated property to my clients, I need to register in the OData Model为了向我的客户提供计算的属性,我需要在 OData 模型中注册

    public static IEdmModel GetModel()
    {
        ODataModelBuilder builder = new ODataConventionModelBuilder();
        builder.Namespace = "NavigationServices";
        builder.EntityType<Person>;   
        builder.EntityType<Person>().Property(a => a.FullName); // Calculated Property
        ....

        return builder.GetEdmModel();
    }

So, when I obtain my data in the client side, every object has the Calculated property.因此,当我在客户端获取数据时,每个对象都具有计算属性。

However, when I try to Create (POST) a new element or Update (PUT) a existing one, my action don't recognize the element and generates an error saying that it doesn't find the "set method" for the property.但是,当我尝试创建 (POST) 一个新元素或更新 (PUT) 一个现有元素时,我的操作无法识别该元素并生成一个错误,指出它找不到该属性的“设置方法”。

I read a couple of posts about read only properties in OData (apparently not supported) but I don't find a way to use OData with calculated properties.我阅读了几篇关于 OData 中只读属性的帖子(显然不支持),但我没有找到将 OData 与计算属性一起使用的方法。

Some advice of how to overcome this situation?关于如何克服这种情况的一些建议?

Now there is a soft way for doing this which is to build a contract between the client and server using annotations.现在有一种软方法可以做到这一点,即使用注释在客户端和服务器之间建立契约。

In the Core vocabulary of the V4 standard , there is a such term:V4 标准核心词汇表中,有这样一个术语:

<Term Name="Computed" Type="Core.Tag" DefaultValue="true" AppliesTo="Property">
    <Annotation Term="Core.Description" String="A value for this property is generated on both insert and update"/>
</Term>

In Web API OData, in WebConfig.cs, you write such code to add such annotation to your property:在 Web API OData 中,在 WebConfig.cs 中,您编写这样的代码来向您的属性添加这样的注释:

ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
var model = builder.GetEdmModel() as EdmModel;
model.SetVocabularyAnnotation(
    new EdmAnnotation(model.EntityContainer.FindEntitySet("People").EntityType().FindProperty("FullName"),
    new EdmTerm("Org.OData.Core.V1", "Computed", EdmPrimitiveTypeKind.Boolean),
    new EdmBooleanConstant(true)));

Then in your data, it'll look something like this:然后在您的数据中,它看起来像这样:

<Annotations Target="V4Service.Models.Person/FullName">
    <Annotation Term="Org.OData.Core.V1.Computed" Bool="true"/>
</Annotations>

Through the steps above the service advertises that the FullName property on Person entity is computed by the service.通过上述步骤,该服务通告了Person实体的FullName属性是由该服务计算的。 Then in the controller methods for POST and PATCH requests you can have you own logic of ignoring any value sent by the client for the FullName property and compute your own.然后在 POST 和 PATCH 请求的控制器方法中,您可以拥有自己的逻辑,忽略客户端为FullName属性发送的任何值并计算您自己的值。

I'm not sure which client you are using.我不确定您使用的是哪个客户端。 If you're using OData Client for .NET , our support for getting annotation values will be in our next release.如果您使用OData Client for .NET ,我们将在下一个版本中支持获取注释值。 If you don't mind using EdmLib directly, the annotation value retrieval support has already been added.如果您不介意直接使用EdmLib ,则已经添加了注释值检索支持。

You are right, OData doesn't support read-only properties at this time.没错, OData目前不支持read-only properties

However, it supports read-only entities .但是,它支持read-only entities

Or, you can trick OData by adding a setter which does nothing to your property .或者,您可以通过添加一个对您的property没有任何作用的setter来欺骗OData

public string FullName
{ 
    get
    {
        return FirstName + " " + LastName;
    }
    set
    {
        // do nothing
    }
}

This is how you set an entity as read-only:这是您将entity设置为只读的方式:

public class Northwind : DataService<NorthwindEntities>
{
    // This method is called only once to initialize service-wide policies.
    public static void InitializeService(DataServiceConfiguration config)
    {
        config.SetEntitySetAccessRule("Customers", EntitySetRights.AllRead);
    }
}

In your entity you need a [NotMapped] data annotation:在您的实体中,您需要一个 [NotMapped] 数据注释:

[NotMapped]
public string FullName => $"{FirstName} - {LastName}";

in your OData configuration:在您的 OData 配置中:

builder.EntityType<Person>;
builder.StructuralTypes.First(t => t.ClrType == typeof(Person))
                .AddProperty(typeof(Person).GetProperty(nameof(Person.FullName)));

this way you don't need an empty setter.这样你就不需要一个空的二传手。

You could use a database computed property instead of a class computed property.您可以使用数据库计算属性而不是类计算属性。

This article describes EF and database computed properties. 本文介绍了 EF 和数据库计算属性。 One difference that I see in your sample code compared to the article is that you don't have any setter in your property.与本文相比,我在示例代码中看到的一个区别是,您的属性中没有任何 setter。 If you set your property to use Auto Accessors with a private set如果您将属性设置为使用带有私有集的自动访问器

public string FullName { 
    get;
    private set; 
}

Then create the column in the database as a computed column.然后在数据库中创建该列作为计算列。

ALTER TABLE dbo.Users ADD FullName AS FirstName + ' ' + LastName

As an additional plus you'll be able to query against this property using odata and Linq.此外,您还可以使用 odata 和 Linq 查询此属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM