ODataException when passing null as a method parameter instead of a decimal value. #3045
vasko-jiri
started this conversation in
General
Replies: 1 comment 1 reply
-
Hello @vasko-jiri. Unfortunately, we are unable to repro the issue. Passing a null to a function works fine. ServiceModelnamespace MinimalService.Models
{
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
} Controllernamespace MinimalService.Controllers
{
using MinimalService.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OData.Routing.Controllers;
public class PeopleController : ODataController
{
[HttpGet]
public ActionResult<IEnumerable<Person>> GetPeopleOfSpecifiedWeightOrMore(decimal? weight)
{
return new List<Person>
{
new Person { Id = 7, Name = "Luc" }
};
}
}
} Service Configurationusing MinimalService.Models;
using Microsoft.AspNetCore.OData;
using Microsoft.OData.ModelBuilder;
var builder = WebApplication.CreateBuilder(args);
var modelBuilder = new ODataConventionModelBuilder();
var personEntityTypeConfiguration = modelBuilder.EntitySet<Person>("People").EntityType;
var getPeopleOfSpecifiedWeightOrMoreFunction = personEntityTypeConfiguration.Collection.Function(
"GetPeopleOfSpecifiedWeightOrMore");
getPeopleOfSpecifiedWeightOrMoreFunction.ReturnsCollectionFromEntitySet(typeof(Person), "People");
getPeopleOfSpecifiedWeightOrMoreFunction.Parameter<decimal?>("weight");
builder.Services.AddControllers().AddOData(
options => options.EnableQueryFeatures().AddRouteComponents(
modelBuilder.GetEdmModel()));
var app = builder.Build();
app.UseRouting();
app.MapControllers();
app.Run(); ClientFunction Invocation Logicusing Default;
var serviceUri = new Uri("http://localhost:5195");
var dataServiceContext = new Container(serviceUri);
var peopleOfSpecifiedWeightOrMore = dataServiceContext.People.GetPeopleOfSpecifiedWeightOrMore(null).ToArray(); Service Proxynamespace MinimalService.Models
{
/// <summary>
/// There are no comments for PersonSingle in the schema.
/// </summary>
[global::Microsoft.OData.Client.OriginalNameAttribute("PersonSingle")]
public partial class PersonSingle : global::Microsoft.OData.Client.DataServiceQuerySingle<Person>
{
/// <summary>
/// Initialize a new PersonSingle object.
/// </summary>
public PersonSingle(global::Microsoft.OData.Client.DataServiceContext context, string path)
: base(context, path) {}
/// <summary>
/// Initialize a new PersonSingle object.
/// </summary>
public PersonSingle(global::Microsoft.OData.Client.DataServiceContext context, string path, bool isComposable)
: base(context, path, isComposable) {}
/// <summary>
/// Initialize a new PersonSingle object.
/// </summary>
public PersonSingle(global::Microsoft.OData.Client.DataServiceQuerySingle<Person> query)
: base(query) {}
}
/// <summary>
/// There are no comments for Person in the schema.
/// </summary>
/// <KeyProperties>
/// Id
/// </KeyProperties>
[global::Microsoft.OData.Client.Key("Id")]
[global::Microsoft.OData.Client.OriginalNameAttribute("Person")]
public partial class Person : global::Microsoft.OData.Client.BaseEntityType, global::System.ComponentModel.INotifyPropertyChanged
{
/// <summary>
/// Create a new Person object.
/// </summary>
/// <param name="ID">Initial value of Id.</param>
/// <param name="name">Initial value of Name.</param>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
public static Person CreatePerson(int ID, string name)
{
Person person = new Person();
person.Id = ID;
person.Name = name;
return person;
}
/// <summary>
/// There are no comments for Property Id in the schema.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
[global::Microsoft.OData.Client.OriginalNameAttribute("Id")]
[global::System.ComponentModel.DataAnnotations.RequiredAttribute(ErrorMessage = "Id is required.")]
public virtual int Id
{
get
{
return this._Id;
}
set
{
this.OnIdChanging(value);
this._Id = value;
this.OnIdChanged();
this.OnPropertyChanged("Id");
}
}
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
private int _Id;
partial void OnIdChanging(int value);
partial void OnIdChanged();
/// <summary>
/// There are no comments for Property Name in the schema.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
[global::Microsoft.OData.Client.OriginalNameAttribute("Name")]
[global::System.ComponentModel.DataAnnotations.RequiredAttribute(ErrorMessage = "Name is required.")]
public virtual string Name
{
get
{
return this._Name;
}
set
{
this.OnNameChanging(value);
this._Name = value;
this.OnNameChanged();
this.OnPropertyChanged("Name");
}
}
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
private string _Name;
partial void OnNameChanging(string value);
partial void OnNameChanged();
/// <summary>
/// This event is raised when the value of the property is changed
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// The value of the property is changed
/// </summary>
/// <param name="property">property name</param>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
protected virtual void OnPropertyChanged(string property)
{
if ((this.PropertyChanged != null))
{
this.PropertyChanged(this, new global::System.ComponentModel.PropertyChangedEventArgs(property));
}
}
}
/// <summary>
/// Class containing all extension methods
/// </summary>
public static class ExtensionMethods
{
/// <summary>
/// Get an entity of type global::MinimalService.Models.Person as global::MinimalService.Models.PersonSingle specified by key from an entity set
/// </summary>
/// <param name="_source">source entity set</param>
/// <param name="_keys">dictionary with the names and values of keys</param>
public static global::MinimalService.Models.PersonSingle ByKey(this global::Microsoft.OData.Client.DataServiceQuery<global::MinimalService.Models.Person> _source, global::System.Collections.Generic.IDictionary<string, object> _keys)
{
return new global::MinimalService.Models.PersonSingle(_source.Context, _source.GetKeyPath(global::Microsoft.OData.Client.Serializer.GetKeyString(_source.Context, _keys)));
}
/// <summary>
/// Get an entity of type global::MinimalService.Models.Person as global::MinimalService.Models.PersonSingle specified by key from an entity set
/// </summary>
/// <param name="_source">source entity set</param>
/// <param name="id">The value of id</param>
public static global::MinimalService.Models.PersonSingle ByKey(this global::Microsoft.OData.Client.DataServiceQuery<global::MinimalService.Models.Person> _source,
int id)
{
global::System.Collections.Generic.IDictionary<string, object> _keys = new global::System.Collections.Generic.Dictionary<string, object>
{
{ "Id", id }
};
return new global::MinimalService.Models.PersonSingle(_source.Context, _source.GetKeyPath(global::Microsoft.OData.Client.Serializer.GetKeyString(_source.Context, _keys)));
}
}
}
namespace Default
{
/// <summary>
/// There are no comments for Container in the schema.
/// </summary>
[global::Microsoft.OData.Client.OriginalNameAttribute("Container")]
public partial class Container : global::Microsoft.OData.Client.DataServiceContext
{
/// <summary>
/// Initialize a new Container object.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
public Container(global::System.Uri serviceRoot) :
this(serviceRoot, global::Microsoft.OData.Client.ODataProtocolVersion.V4)
{
}
/// <summary>
/// Initialize a new Container object.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
public Container(global::System.Uri serviceRoot, global::Microsoft.OData.Client.ODataProtocolVersion protocolVersion) :
base(serviceRoot, protocolVersion)
{
this.ResolveName = new global::System.Func<global::System.Type, string>(this.ResolveNameFromType);
this.ResolveType = new global::System.Func<string, global::System.Type>(this.ResolveTypeFromName);
this.OnContextCreated();
this.Format.LoadServiceModel = GeneratedEdmModel.GetInstance;
this.Format.UseJson();
}
partial void OnContextCreated();
/// <summary>
/// Since the namespace configured for this service reference
/// in Visual Studio is different from the one indicated in the
/// server schema, use type-mappers to map between the two.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
protected global::System.Type ResolveTypeFromName(string typeName)
{
global::System.Type resolvedType = this.DefaultResolveType(typeName, "MinimalService.Models", "MinimalService.Models");
if ((resolvedType != null))
{
return resolvedType;
}
resolvedType = this.DefaultResolveType(typeName, "Default", "Default");
if ((resolvedType != null))
{
return resolvedType;
}
return null;
}
/// <summary>
/// Since the namespace configured for this service reference
/// in Visual Studio is different from the one indicated in the
/// server schema, use type-mappers to map between the two.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
protected string ResolveNameFromType(global::System.Type clientType)
{
global::Microsoft.OData.Client.OriginalNameAttribute originalNameAttribute = (global::Microsoft.OData.Client.OriginalNameAttribute)global::System.Linq.Enumerable.SingleOrDefault(global::Microsoft.OData.Client.Utility.GetCustomAttributes(clientType, typeof(global::Microsoft.OData.Client.OriginalNameAttribute), true));
if (clientType.Namespace.Equals("MinimalService.Models", global::System.StringComparison.Ordinal))
{
if (originalNameAttribute != null)
{
return string.Concat("MinimalService.Models.", originalNameAttribute.OriginalName);
}
return string.Concat("MinimalService.Models.", clientType.Name);
}
if (clientType.Namespace.Equals("Default", global::System.StringComparison.Ordinal))
{
if (originalNameAttribute != null)
{
return string.Concat("Default.", originalNameAttribute.OriginalName);
}
return string.Concat("Default.", clientType.Name);
}
return null;
}
/// <summary>
/// There are no comments for People in the schema.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
[global::Microsoft.OData.Client.OriginalNameAttribute("People")]
public virtual global::Microsoft.OData.Client.DataServiceQuery<global::MinimalService.Models.Person> People
{
get
{
if ((this._People == null))
{
this._People = base.CreateQuery<global::MinimalService.Models.Person>("People");
}
return this._People;
}
}
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
private global::Microsoft.OData.Client.DataServiceQuery<global::MinimalService.Models.Person> _People;
/// <summary>
/// There are no comments for People in the schema.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
public virtual void AddToPeople(global::MinimalService.Models.Person person)
{
base.AddObject("People", person);
}
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
private abstract class GeneratedEdmModel
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
private static global::Microsoft.OData.Edm.IEdmModel ParsedModel = LoadModelFromString();
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
private const string filePath = @"MinimalServiceCsdl.xml";
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
public static global::Microsoft.OData.Edm.IEdmModel GetInstance()
{
return ParsedModel;
}
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
private static global::Microsoft.OData.Edm.IEdmModel LoadModelFromString()
{
global::System.Xml.XmlReader reader = CreateXmlReader();
try
{
global::System.Collections.Generic.IEnumerable<global::Microsoft.OData.Edm.Validation.EdmError> errors;
global::Microsoft.OData.Edm.IEdmModel edmModel;
if (!global::Microsoft.OData.Edm.Csdl.CsdlReader.TryParse(reader, true, out edmModel, out errors))
{
global::System.Text.StringBuilder errorMessages = new global::System.Text.StringBuilder();
foreach (var error in errors)
{
errorMessages.Append(error.ErrorMessage);
errorMessages.Append("; ");
}
throw new global::System.InvalidOperationException(errorMessages.ToString());
}
return edmModel;
}
finally
{
((global::System.IDisposable)(reader)).Dispose();
}
}
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
private static global::System.Xml.XmlReader CreateXmlReader(string edmxToParse)
{
return global::System.Xml.XmlReader.Create(new global::System.IO.StringReader(edmxToParse));
}
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "#VersionNumber#")]
private static global::System.Xml.XmlReader CreateXmlReader()
{
try
{
var assembly = global::System.Reflection.Assembly.GetExecutingAssembly();
var resourcePath = global::System.Linq.Enumerable.Single(assembly.GetManifestResourceNames(), str => str.EndsWith(filePath));
global::System.IO.Stream stream = assembly.GetManifestResourceStream(resourcePath);
return global::System.Xml.XmlReader.Create(new global::System.IO.StreamReader(stream));
}
catch(global::System.Xml.XmlException e)
{
throw new global::System.Xml.XmlException("Failed to create an XmlReader from the stream. Check if the resource exists.", e);
}
}
}
}
/// <summary>
/// Class containing all extension methods
/// </summary>
public static class ExtensionMethods
{
/// <summary>
/// There are no comments for GetPeopleOfSpecifiedWeightOrMore in the schema.
/// </summary>
[global::Microsoft.OData.Client.OriginalNameAttribute("GetPeopleOfSpecifiedWeightOrMore")]
public static global::Microsoft.OData.Client.DataServiceQuery<global::MinimalService.Models.Person> GetPeopleOfSpecifiedWeightOrMore(this global::Microsoft.OData.Client.DataServiceQuery<global::MinimalService.Models.Person> _source, global::System.Nullable<decimal> weight)
{
if (!_source.IsComposable)
{
throw new global::System.NotSupportedException("The previous function is not composable.");
}
return _source.CreateFunctionQuery<global::MinimalService.Models.Person>("Default.GetPeopleOfSpecifiedWeightOrMore", false, new global::Microsoft.OData.Client.UriOperationParameter("weight", weight));
}
}
} Csdl - MinimalServiceCsdl.xml<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices>
<Schema Namespace="MinimalService.Models" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityType Name="Person">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.Int32" Nullable="false" />
<Property Name="Name" Type="Edm.String" Nullable="false" />
</EntityType>
</Schema>
<Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<Function Name="GetPeopleOfSpecifiedWeightOrMore" IsBound="true">
<Parameter Name="bindingParameter" Type="Collection(MinimalService.Models.Person)" />
<Parameter Name="weight" Type="Edm.Decimal" Scale="variable" />
<ReturnType Type="Collection(MinimalService.Models.Person)" />
</Function>
<EntityContainer Name="Container">
<EntitySet Name="People" EntityType="MinimalService.Models.Person" />
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx> |
Beta Was this translation helpful? Give feedback.
1 reply
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Dear members, can you please assist me in finding a solution for my struggling situation?
I am using OData CodeGenerator Tools to generate proxy classes for our ERP system's API. There is this endpoint/method "GetOperations" with multiple arguments, and some of them are nullable.
/// <summary> /// There are no comments for GetOperations in the schema. /// </summary> [global::Microsoft.OData.Client.OriginalNameAttribute("GetOperations")] public virtual global::Microsoft.OData.Client.DataServiceQuery<global::IfsApp.ShopFloorWorkbenchHandling.ShopOrderOperation> GetOperations(string Contract, global::System.Nullable<global::IfsApp.ShopFloorWorkbenchHandling.DispatchListFilterBy> FilterBy, string DispListFilterId, global::System.Nullable<global::IfsApp.ShopFloorWorkbenchHandling.DisListFilterSelection> Selection, global::System.Nullable<global::IfsApp.ShopFloorWorkbenchHandling.DispatchRule> DispatchRule, string DepartmentList, string WorkCenterList, global::System.Nullable<global::IfsApp.ShopFloorWorkbenchHandling.WorkCenterCodeShopFloor> WorkCenterCode, string ProductionLineList, string ResourceList, string LaborClassList, global::System.Nullable<global::Microsoft.OData.Edm.Date> DateFrom, global::System.Nullable<global::Microsoft.OData.Edm.Date> DateTo, global::System.Nullable<global::IfsApp.ShopFloorWorkbenchHandling.DispListIntervalBasis> BaseIntervalOn, global::System.Nullable<bool> MyAssignedOper, string BarcodeId, string OrderNo, string ReleaseNo, string SequenceNo, string ProgramId, string ProjectId, string SubProjectId, string ActivityNo, global::System.Nullable<decimal> ActivitySeq, string CompanyId, string EmployeeId, string TeamId) { return this.CreateFunctionQuery<global::IfsApp.ShopFloorWorkbenchHandling.ShopOrderOperation>("", "GetOperations", false, new global::Microsoft.OData.Client.UriOperationParameter("Contract", Contract), new global::Microsoft.OData.Client.UriOperationParameter("FilterBy", FilterBy), new global::Microsoft.OData.Client.UriOperationParameter("DispListFilterId", DispListFilterId), new global::Microsoft.OData.Client.UriOperationParameter("Selection", Selection), new global::Microsoft.OData.Client.UriOperationParameter("DispatchRule", DispatchRule), new global::Microsoft.OData.Client.UriOperationParameter("DepartmentList", DepartmentList), new global::Microsoft.OData.Client.UriOperationParameter("WorkCenterList", WorkCenterList), new global::Microsoft.OData.Client.UriOperationParameter("WorkCenterCode", WorkCenterCode), new global::Microsoft.OData.Client.UriOperationParameter("ProductionLineList", ProductionLineList), new global::Microsoft.OData.Client.UriOperationParameter("ResourceList", ResourceList), new global::Microsoft.OData.Client.UriOperationParameter("LaborClassList", LaborClassList), new global::Microsoft.OData.Client.UriOperationParameter("DateFrom", DateFrom), new global::Microsoft.OData.Client.UriOperationParameter("DateTo", DateTo), new global::Microsoft.OData.Client.UriOperationParameter("BaseIntervalOn", BaseIntervalOn), new global::Microsoft.OData.Client.UriOperationParameter("MyAssignedOper", MyAssignedOper), new global::Microsoft.OData.Client.UriOperationParameter("BarcodeId", BarcodeId), new global::Microsoft.OData.Client.UriOperationParameter("OrderNo", OrderNo), new global::Microsoft.OData.Client.UriOperationParameter("ReleaseNo", ReleaseNo), new global::Microsoft.OData.Client.UriOperationParameter("SequenceNo", SequenceNo), new global::Microsoft.OData.Client.UriOperationParameter("ProgramId", ProgramId), new global::Microsoft.OData.Client.UriOperationParameter("ProjectId", ProjectId), new global::Microsoft.OData.Client.UriOperationParameter("SubProjectId", SubProjectId), new global::Microsoft.OData.Client.UriOperationParameter("ActivityNo", ActivityNo), new global::Microsoft.OData.Client.UriOperationParameter("ActivitySeq", ActivitySeq), new global::Microsoft.OData.Client.UriOperationParameter("CompanyId", CompanyId), new global::Microsoft.OData.Client.UriOperationParameter("EmployeeId", EmployeeId), new global::Microsoft.OData.Client.UriOperationParameter("TeamId", TeamId)); }
The issue is when I pass a null to a non-string argument, like ActivitySeq which is decimal?, I get the following exception:
So it seems when there is e.g. a decimal? type, and I pass null, the exception is thrown. The code runs well if I pass some numeric value there, e.g. 0. But I need to be able to pass null as well.
From ChatGPT I get some explanation that there is probably an issue when serializing the response, that it is difficult to determine type from the null value, but no advice has helped me yet. And now it is unfortunately above my skills.
Is there please any solution on how to pass also the null as a value?
Beta Was this translation helpful? Give feedback.
All reactions