Search code examples
c#odataasp.net-web-api2odata-v4

.Net OData v4 Client Generation - IDataErrorInfo


I have created an OData v4 Client with the OData Client Generator. This generated partial classes. I would like to extend this generated classes with IDataErrorInfo.

namespace Client.Model {
    public partial class City : IDataErrorInfo
    {
        public String this[String columnName]
        {
            return "";
        }

        public String Error { get { return ""; } }
    }
}

When i like to create a new City and send it to the server

ODataContainer container = new ODataContainer(new Uri("http://localhost:45666/odata"));
container.AddToCities(city);

I get an error

An exception of type 'Microsoft.OData.Client.DataServiceRequestException' occurred in Microsoft.OData.Client.dll.

The request is invalid. The property "Error" does not exist in Server.Model.City.

The WebApi configuration:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
            );

        ODataModelBuilder builder = new ODataConventionModelBuilder();
        builder.EntitySet<City>("Cities");
        builder.EntitySet<Country>("Countries");

        config.MapODataServiceRoute(
            routeName: "ODataRoute",
            routePrefix: "odata",
            model: builder.GetEdmModel());
    }
}

Is there a possibilty to prevent the Error property being included in the request?


Solution

  • The Model generated with the OData client are partial classes. When you implement the IDataErrorInfo then it would ask you to implement the Error property which ofcourse is not present at server side. Because you do an operation e.g. on entity City this serialize the City object, and if there's an Error property it will get serialized too.

    A solution could be, to avoid this situation and keep the Client models separate from the UI. You can try this:

    namespace Client.Model {
        public partial class City
        {
            public String this[String columnName]
            {
                return "";
            }
        }
    }
    

    Use inheritance to create the UI related model classes, separate from the generated ones:

    namespace UI.Model {
        public class City : Client.Model.City, IDataErrorInfo
        {
            public String Error { get { return ""; } }
        }
    }
    

    Make sure you use UI.Model.City on UI and when you do call the OData service for Add operation, Perform an explicit cast on UI.Model.City class object to convert to Client.Model.City and the Error property will be gone:

    ODataContainer container = new ODataContainer(new Uri("http://localhost:45666/odata"));
    container.AddToCities((Client.Model.City)city);
    

    Note: This approach have its own Cons as it might lead you to have same class names under different namespaces so you often have to use full namespace path when using same class names. You can avoid it using different prefix/postfix with Class name on UI models. e.g. CityViewModel.