Blazor Child Component Rendering and Parameter Changes - data-binding

I have a component that accepts an int parameter - which the component uses to make an API call to retrieve some data. This logic is currently in the component's OnParametersSetAsync().
This component also has a complex-typed parameter.
When this component is used by a parent component that re-renders itself for various reasons, OnParametersSetAsync() is called on this child component - even if none of its parameters have changed. My understanding is that this is because of the complex-type parameter (blazor can't tell if it changed, so it assumes it did).
This results in the API call retriggering needlessly (the actual int parameter didn't change).
Is doing data-retrieval like this not appropriate for OnParametersSetAsync()? If so, how should I change my components to work with the Blazor framework?
Parent Component
Call to ChangeName() triggers the re-render of the parent
<div>
<EditForm Model="favoriteNumber">
<InputSelect #bind-Value="favoriteNumber">
<option value="0">zero</option>
<option value="1">one</option>
<option value="2">two</option>
<option value="3">three</option>
</InputSelect>
</EditForm>
#* This is the child-component in question *#
<TestComponent FavoriteNumber="favoriteNumber" FavoriteBook="favoriteBook" />
<br />
<EditForm Model="person">
First Name:
<InputText #bind-Value="person.FirstName" />
<br />
Last Name:
<InputText #bind-Value="person.LastName" />
</EditForm>
<button #onclick="ChangeName">Change Name</button>
</div>
#code {
private int favoriteNumber = 0;
private Book favoriteBook = new();
private Person person = new() { FirstName = "Joe", LastName = "Smith" };
private void ChangeName()
{
person.FirstName = person.FirstName == "Susan" ? "Joe" : "Susan";
person.LastName = person.LastName == "Smith" ? "Williams" : "Smith";
}
}
Child Component
<div>#infoAboutFavoriteNumber</div>
#code {
[Parameter]
public int FavoriteNumber { get; set; }
[Parameter]
public Book FavoriteBook { get; set; }
private string infoAboutFavoriteNumber = "";
protected override async Task OnParametersSetAsync()
{
infoAboutFavoriteNumber = await ApiService.GetAsync<string>(id: FavoriteNumber.ToString());
}
}

which the component uses to make an API call to retrieve some data.
Your child component should not perform any API calls. It is the parent component that should manage the state of the parent itself and its children, downstreaming the data. If things get complicated, then you'll have to implement a service that handle state. #Peter Morris would certainly advise you to use Blazor State Management Using Fluxor.
Not sure why you use two EditForm components, when you actually should use none. Realize that components are very expensive, and they make your code slow. So use it wisely
Answering your question:
Define a local field to hold the FavoriteNumber parameter property's value as follows, in the child component:
#code
{
[Parameter]
public int FavoriteNumber { get; set; }
private int FavoriteNumberLocal = -1;
}
Note: The FavoriteNumberLocal variable stores the value passed from the parent component. It allows you to locally store and check if its value has changed, and accordingly decide whether to call the Web Api end point or not (Again, you shouldn't do that like this)
protected override async Task OnParametersSetAsync()
{
if( FavoriteNumberLocal != FavoriteNumber)
{
FavoriteNumberLocal = FavoriteNumber;
infoAboutFavoriteNumber = await ApiService.GetAsync<string>(id:
FavoriteNumberLocal.ToString());
}
}
Read the last two comments to this question

You can implement your own state logic with a private int.
A lot cheaper than calling an API again.
<div>#infoAboutFavoriteNumber</div>
#code {
[Parameter]
public int FavoriteNumber { get; set; }
[Parameter]
public Book FavoriteBook { get; set; }
private string infoAboutFavoriteNumber = "";
private int currentNumber = -1; // some invalid value
protected override async Task OnParametersSetAsync()
{
if (currentNumber != FavoriteNumber)
{
currentNumber = FavoriteNumber;
infoAboutFavoriteNumber = await ApiService.GetAsync<string>(id: FavoriteNumber.ToString());
}
}
}

I don't think that this is necessarily a poor practice to put this logic into the OnParametersSetAsync() method. But there is a way to prevent it from making so many API calls. I would create a private variable that stores the value of the public parameter and then every time the OnParametersSetAsync() method is called you compare the two variables and if they are the same, then you don't make the API call, if they are different, then you make the API call, and after it finishes, you assign the private variable to the public parameter's value. To account for the very first time the component calls the method I would probably assign the private variable to default to a -1, as typically ID values aren't negative. But basically I would assign it to a value that would never be equal to any value passed as the parameter. Otherwise the first time it is called your API might not actually get called. Here is an example:
<div>#infoAboutFavoriteNumber</div>
#code {
[Parameter]
public int FavoriteNumber { get; set; }
private int CurrentFavoriteNumber { get; set; } = -1;
[Parameter]
public Book FavoriteBook { get; set; }
private string infoAboutFavoriteNumber = "";
protected override async Task OnParametersSetAsync()
{
if (FavoriteNumber != CurrentFavoriteNumber)
{
infoAboutFavoriteNumber = await ApiService.GetAsync<string>(id: FavoriteNumber.ToString());
CurrentFavoriteNumber = FavoriteNumber;
}
}
}

You can introduce local field and compare its value like other suggests or catch the old value before it changes in SetParametersAsync and it will work in basic scenarios.
However, what if:
the parameter changes too quickly? You will get concurrent requests and the response can come in wrong order.
you leave the page and the response come later?
you want to delay or throttle the parameter changes, e.g. when the parameter is bound to user input.
Reactive Extensions (IObservable) is designed to deal exactly with such scenarios. In Angular (very simmilar to Blazor), the RxJS is first class citizen.
In Blazor, just turn the parameter into IObservable, use RX Operators to deal with it without introducing your own local variables.
readonly Subject<Unit> _parametersSet = new ();
protected override Task OnParametersSetAsync()
{
_parametersSet.OnNext(Unit.Default); //turn OnParametersSetAsync into Observable stream
return base.OnParametersSetAsync();
}
[Parameter] public int FavoriteNumber { get; set; }
protected override void OnInitialized()
{
_parametersSet.Select(_ => FavoriteNumber) //turn parameter into Observable
.DistinctUntilChanged() //detect changes
.Select(value => Observable.FromAsync(cancellationToken =>
{
Console.WriteLine($"FavoriteNumber has changed: {value}");
infoAboutFavoriteNumber = await ApiService.GetAsync(value, cancellationToken);
})
.Switch() //take care of concurrency
.Subscribe();
}
The nice thing about it, that you can create a reusable class or helper method with all the boilerplate. You just specify a parameter and the async method, e.g:
Loader.Create(ObserveParameter(() => FavoriteNumber), LoadAsync);
for more reading, check:
this article: https://blog.vyvojari.dev/blazor-take-advantage-of-system-reactive-aka-observables-part-2/
live demo: https://blazorrepl.telerik.com/QQullYbo21fLFclq27
this question: How to add "reload" and IsLoading status to 2nd level Observable

You're facing a common problem: doing data and data access activity in the UI. Things tend to get messy! In this answer I've separated the data from the components. The data and data access reside in a Dependancy Injection service.
I've also done away with EditForm as you aren't actually using it, and changed the Select to a simple select so we can capture updates, update the model and trigger the data retrieval in the service. This also means that the component gets re-rendered after the model has updated. The Blazor UI event handler for the OnChanged event calls StateHasChanged after calling NumberChanged.
First a class for our Favourites data.
public class MyFavourites
{
public int FavouriteNumber { get; set; }
public string FavouriteNumberInfo { get; set; } = string.Empty;
}
Second a DI service to hold our Favourites data and datastore operations.
namespace Server;
public class MyFavouritesViewService
{
public MyFavourites Favourites { get; private set; } = new MyFavourites();
public async Task GetFavourites()
{
// Emulate a database get
await Task.Delay(100);
Favourites = new MyFavourites { FavouriteNumber = 2, FavouriteNumberInfo = "The number is 2" };
}
public async Task SaveFavourites()
{
// Emulate a database save
await Task.Delay(100);
// Save code here
}
public async Task GetNewNumberInfo(int number)
{
if (number != Favourites.FavouriteNumber)
{
// Emulate a database get
await Task.Delay(100);
Favourites.FavouriteNumberInfo = $"The number is {number}";
Favourites.FavouriteNumber = number;
}
}
}
Next register the Service in Program:
builder.Services.AddScoped<MyFavouritesViewService>();
The component:
<h3>MyFavouriteNumber is #this.Favourites.FavouriteNumber</h3>
<h3>MyFavouriteNumber info is #this.Favourites.FavouriteNumberInfo</h3>
#code {
[Parameter][EditorRequired] public MyFavourites Favourites { get; set; } = new MyFavourites();
}
And finally the page. Note I'm using OwningComponentBase to tie the scope of MyFavouritesViewService to the component lifecycle.
#page "/favourites"
#page "/"
#inherits OwningComponentBase<MyFavouritesViewService>
#namespace Server
<h3>Favourite Number</h3>
<div class="p-5">
<select class="form-select" #onchange="NumberChanged">
#foreach (var option in options)
{
if (option.Key == this.Service.Favourites.FavouriteNumber)
{
<option selected value="#option.Key">#option.Value</option>
}
else
{
<option value="#option.Key">#option.Value</option>
}
}
</select>
<div>
<button class="btn btn-success" #onclick="SaveFavourites">Save</button>
</div>
</div>
<MyFavouriteNumber Favourites=this.Service.Favourites />
#code {
private Dictionary<int, string> options = new Dictionary<int, string>
{
{0, "Zero"},
{1, "One"},
{2, "Two"},
{3, "Three"},
};
// Use OnInitializedAsync to get the original values from the data store
protected async override Task OnInitializedAsync()
=> await this.Service.GetFavourites();
// Demo to show saving
private async Task SaveFavourites()
=> await this.Service.SaveFavourites();
// Async setup ensures GetNewNumberInfo runs to completion
// before StatehasChanged is called by the Handler
// Renderer the checks what's changed and calls SetParamaterAsync
// on MyFavouriteNumber because FavouriteNumber has changed
private async Task NumberChanged(ChangeEventArgs e)
{
if (int.TryParse(e.Value?.ToString(), out int value))
await this.Service.GetNewNumberInfo(value);
}
}

Related

Due to Blazor Change Detection API, component re-rendering get skipped. How to avoid?

I am working on Blazor project (.NET 5). I got a problem with components rendering.
I have parent component with ChildContent as RenderFragment inside. And I use it like this:
<ParentComponent>
<ChildComponent1 Title="Component1"></ChildComponent1>
<ChildComponent2 Title="Component2" SampleEnum="SampleEnum.Bar"></ChildComponent2>
</ParentComponent>
Each ChildComponent inherits ChildComponentBase:
public class ChildComponent1 : ChildComponentBase
{
// some code
}
ChildComponentBase contains ParentComponent as cascading parameter and 2 parameters: one of them is string (Immutable for Blazor Change Detection API) and another one is enum (which is not Immutable) just for sake of example. And here we also
public partial class ChildComponentBase
{
[CascadingParameter]
public ParentComponent Parent { get; set; } = default !;
[Parameter]
public string? Title { get; set; } // Immutable
[Parameter]
public SampleEnum SampleEnum { get; set; } // not Immutable
}
In ParentComponent I use a strategy of deferred rendering. Defer component looks like this and being used in ParentComponent:
// This is used to move its body rendering to the end of the render queue so we can collect
// the list of child components first.
public class Defer : ComponentBase
{
[Parameter]
public RenderFragment ChildContent { get; set; }
protected override void BuildRenderTree( RenderTreeBuilder builder )
{
builder.AddContent( 0, ChildContent );
}
}
In my project on first render I collect all ChildComponent from ChildContent like this:
ChildComponentBase.razor
#{
Parent.AddChild(this); // Parent is cascading parameter
}
And then I invoke a callback to process data. ParentComponent looks like this:
ParentComponent.razor
<CascadingValue Value="this" IsFixed>
#{
StartCollectingChildren();
}
#ChildContent
<Defer>
#{
FinishCollectingChildren();
ProcessDataAsync();
}
#foreach (var o in _childComponents)
{
<p>#o.Title</p>
}
</Defer>
</CascadingValue>
ParentComponent.razor.cs
public partial class ParentComponent
{
[Parameter]
public RenderFragment ChildContent { get; set; }
private List<ChildComponentBase> _childComponents = new();
private bool _firstRender = true;
private bool _collectingChildren; // Children might re-render themselves arbitrarily. We only want to capture them at a defined time.
protected async Task ProcessDataAsync()
{
if (_firstRender)
{
//imitating re-render just like it would be an async call
await InvokeAsync(StateHasChanged);
_firstRender = false;
}
}
public void AddChild(ChildComponentBase child)
{
_childComponents.Add(child);
}
private void StartCollectingChildren()
{
_childComponents.Clear();
_collectingChildren = true;
}
private void FinishCollectingChildren()
{
_collectingChildren = false;
}
}
Due to invoke of callback - re-rendering happens. And due to re-rendering StartCollectingChildren() is getting called again. This time on second render of ParentComponent the ChildComponent1 doesn't re-render, because Blazor Change Detection API skips it (because it contains only an Immutable parameter Title while ChildComponent2 in addition contains enum parameter).
Question: how to make this ChildComponent1 get re-rendered anyway?
I also added a Sample Project with code described above for you to try it out yourself.
I tried everything I could find in the google. The best workaround I found is to cache children collection on first render, but it looks dirty and could cause issues in a future.
The quick fix to your problem is to modify the cascade and remove IsFixed.
Once you do that any component that captures the cascade will always be rendered because this is an object and therefore fails the equality check.
You can also drive render events on sub components that don't have object parameters using a Guid Cascade. Assign a new Guid to the mapped parameter whenever you want to force a render on any sub component the captures the cascade.

EF Core with CosmosDB: OwnsOne and OwnsMany throw NullReferenceException

I'm working on a new project that uses CosmosDB and Entity Framework Core (via the Microsoft.EntityFrameworkCore.Cosmos NuGet package, version 5.0.7; the project itself is .NET Core 5). I'm new to both, and running into an issue I can't sort out.
In short, I need to save a complex object to the database. It's a big model that will have multiple collections of classes underneath it, each with their own properties and some with collections underneath them as well. I'm trying to configure EF with OwnsOne and OwnsMany to store these child objects underneath the top-level one. The code compiles, and will save to the database so long as all the owned objects are left empty. But whenever I put anything into an owned object, either with OwnsOne or OwnsMany, I get a pair of NullReferenceExceptions.
I've tried to strip my code down to the very basics. Here's how it currently looks.
Owner and owned classes:
public class Questionnaire
{
// Constructors
private Questionnaire() { }
public Questionnaire(Guid id)
{
Test = "Test property.";
TV = new TestQ();
Id = id;
}
public Guid Id { get; set; }
public string Test { get; set; }
public TestQ TV { get; set; }
// Public Methods
public void AddForm(Form f)
{
// not currently using this method
//Forms.Add(f);
}
}
public class TestQ
{
public TestQ()
{
TestValue = "test ownsone value";
}
public string TestValue { get; set; }
}
DbContext:
public class QuestionnaireDbContext : DbContext
{
public DbSet<Questionnaire> Questionnaires { get; set; }
public QuestionnaireDbContext(DbContextOptions<QuestionnaireDbContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultContainer(nameof(Questionnaires));
modelBuilder.Entity<Questionnaire>().HasKey(q => q.Id);
modelBuilder.Entity<Questionnaire>().OwnsOne(q => q.TV);
}
}
And the code from the service that calls the dbContext (note that this is based on a generic service that I didn't set up originally). The actual exceptions are thrown here.
public virtual TEntity Add(TEntity entity)
{
_context.Entry(entity).State = EntityState.Added;
_context.SaveChanges();
return entity;
}
Ultimately I need this to work with OwnsMany and a collection, but I figured it might be simpler to get it working with OwnsOne first. The key thing to note here is that if I comment out the line
TV = new TestQ();
in the Questionnaire class, the model persists correctly into CosmosDB. It's only when I actually instantiate an owned entity that I get the NullReferenceExceptions.
Any advice would be much appreciated! Thank you!
Well, I'm not sure why this is the case, but the issue turned out to be with how we were adding the document. Using this generic code:
public virtual async Task<TEntity> Add(TEntity entity)
{
_context.Entry(entity).State = EntityState.Added;
await _context.SaveChanges();
return entity;
}
was the issue. It works just fine if I use the actual QuestionnaireDbContext class like so:
context.Add(questionnaire);
await context.SaveChangesAsync();

Blazor get nested components by Reflection

I'm actually working on form validation in Blazor project (0.8.0).
I have created a component called InputValidation. This component receive many parameters to test if a property value is correct according a condition we can set up.
#using System.Linq.Expressions;
#typeparam TItem
#if (!Valid)
{
<span id="#(Id)_validation" class="form-text text-danger">#Message</span>
}
#functions {
[Parameter]
string Id { get; set; }
[Parameter]
TItem Property { get; set; }
[Parameter]
Expression<Func<TItem, bool>> On { get; set; }
[Parameter]
string Message { get; set; }
[Parameter]
bool ActiveOnLoad { get; set; } = true;
internal bool Valid { get; set; }
bool Activated;
protected async override Task OnInitAsync()
{
Activated = ActiveOnLoad;
}
protected async override Task OnAfterRenderAsync()
{
Activated = true;
}
protected async override Task OnParametersSetAsync()
{
Valid = !On.Compile().Invoke(Property);
}
}
You can implement it on your parent component like this :
<InputValidation Id="#nameof(ViewModel.UrlInput)" Property="#ViewModel.UrlInput" On="#(x => string.IsNullOrEmpty(x))" Message="Url is empty" ActiveOnLoad="#false"/>
I have coded a class that verifies that all InputValidation components have the property Valid at true.
#if (ViewModel.IsValid(this))
this represents the parent component.
The problem is... it's not working !
Here is the code of the validator :
public static class ModelValidator
{
public static bool IsValid<T, V>(this T viewmodel, V component) where T : IViewModel where V : ComponentBase
=> component.GetType().GetFields().OfType<InputValidation<T>>().All(x => x.Valid);
}
It's not working, I know, but even if we use Reflection (GetProperties, GetFields, GetMembers), it won't return any of the InputValidation members of the parent component.
My question is : is there a way to get all child components by using Reflection ? If yes, how to do it ?
I know that Blazor is still on early stage and I hope it will be released soon because it's a very pleasant technology !
Thank you for your responses !
You don't need reflection here (the InputValidation component is not a field in the parent, it is a component that will be rendered by the RenderTree).
You can capture a reference to each InputValidation component using the ref attribute.
<InputValidation ref="#InputValidationRef" Id="#nameof(ViewModel.UrlInput)" Property="#ViewModel.UrlInput" On="#(x => string.IsNullOrEmpty(x))" Message="Url is empty" ActiveOnLoad="#false"/>
Normally this ref "InputValidationRef" would be a field, but you can, instead use a property with a custom setter to build a list (or whatever collection you like)
List<InputValidation> InputValidations = new List<InputValidation>();
InputValidation InputValidationRef { set => InputValidations.Add(value); }
So, each InputValidation will now be captured as a reference and the Property InputValidationRef will be set for each one, which will, in turn, store all the references in the collection "InputValidations".
Now, you have a collection, you can test against
InputValidations.All(iv => iv.Valid)
Note: the collection is only populated after the component/page is rendered, so during the initial page load the collection of references is empty until the OnAfterRender/OnAfterRenderAsync method is called.

MassTransit. Consume equal objects defined in different namespaces

First of all, excuse my English, it's very bad. I am using MassTransit with Azure Service Bus for asynchronous communication between microservices. By their own definition, and to avoid generating dependencies between them, messages sent between different microservices are defined in each of them, that is, they are part of different namespaces. The automatic management of MassTransit causes queues and topics to be managed by the object type, which prevents the microservices that consume a message from receiving the messages sent by the microservice publisher. The same thing happens with two classes with the same properties in the same namespace but with a different class name.
Is there any way to solve this?
The options that have occurred to me are:
Remove the namespace from the endpoint of the destination address, naming it only with the name of the class.
That MassTransit can manage the creation of queues and topics based on the serialization of the object, instead of managing it based on the object type (perhaps through some type of wrapping object?)
I leave an example that I hope can help you in understanding the problem.
//FIRST PROGRAM - MESSAGE CONSUMER
namespace Consumer
{
public class Example
{
public string PropOne { get; set; }
public string PropTwo { get; set; }
}
public class ExampleConsumer :
IConsumer<Example>
{
public List<Example> ConsumedTestObjectList { get; } = new List<Example>();
//THIS METHOD NEVER CALL !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
public Task Consume(ConsumeContext<ExampleConsumer> context)
{
ConsumedTestObjectList.Add(context.Message);
return Task.CompletedTask;
}
}
public class ConsumerProgram
{
public static void Main()
{
var bus = Bus.Factory.CreateUsingAzureServiceBus(sbc =>
{
var host = sbc.Host("connectionString", h => {});
});
sbc.ReceiveEndpoint(host, e =>
{
e.Consumer<ConsumerProgram.Example>(context =>
{
return Console.Out.WriteLineAsync($"Message Received: {JsonConvert.SerializeObject(context.Message)}");
});
});
bus.Start(); // This is important!
Console.WriteLine("Press any key to exit");
Console.ReadKey();
bus.Stop();
}
}
}
//SECOND PROGRAM - MESSAGE PUBLISHER
namespace Publisher
{
public class Example
{
public string PropOne { get; set; }
public string PropTwo { get; set; }
}
public class PublisherProgram
{
public static void Main()
{
var bus = Bus.Factory.CreateUsingAzureServiceBus(sbc =>
{
var host = sbc.Host("connectionString", h => {});
});
bus.Start(); // This is important!
//send new instance of Publisher.Example
var example = new Example() { PropOne = "1", PropTwo = "2" };
bus.Publish(example);
Console.WriteLine("Press any key to exit");
Console.ReadKey();
bus.Stop();
}
}
}
Thank you very much.
regards
Borja
The message type, and the resulting name, are a key concept within MassTransit. If you want to avoid sharing assemblies between projects, that is fine, but you will need to match the entire interface (or class, in your case) name, including namespace, or it will not route properly.
Yes, you can override the entity name formatter to change how topics are named but it won't change the message type requirement for deserialization of the message (which happens, by type).
So the recommendation here is to use the same namespace for the contracts, even if they're in separate projects.

OData paging with expand issue

I'm using OData v5/Web API 2.2 to create an endpoint that will return a list of employees from each company.
My problem occurs when I try to implement server-side paging while also using the OData $expand property. When I try to make a call to
http://localhost:60067/Companies?$expand=Employees
I get an error that says "Could not find a property named 'Employees' on type 'System.Web.OData.Query.Expressions.SelectAllAndExpand_1OfCompanyApiModel'"
However, when I removed the EnableQuery attribute the call to the endpoint or when I didn't expand it works as expected. Does anyone have an idea of what I am doing wrong? I've been googling this for a while but haven't found anything.
Here are some code snippets -
Data Models:
public class CompanyApiModel
{
[Key]
public Guid CompanyGuid { get; set; }
[Required]
public string Name { get; set; }
// other properties
public List<EmployeeApiModel> Employees { get; set; }
}
public class EmployeeApiModel
{
[Key]
public Guid EmployeeGuid { get; set; }
[Required]
public string Name { get; set; }
// other properties
}
CompaniesController.cs:
[EnableQuery(PageSize = 10)] // If I comment this out everything works
//[EnableQuery] // This fails as well
public IHttpActionResult Get(ODataQueryOptions<CompanyApiModel> queryOptions)
{
var companies = GetCompanies(queryOptions);
return Ok(companies);
// return Ok(companies.AsQueryable()); // This doesn't work either
}
WebApiConfig.cs:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
var routingConventions = ODataRoutingConventions.CreateDefault();
routingConventions.Insert(0, new OptionsRoutingConvention());
config.MapODataServiceRoute("odata", null, GetEdmModel(), new DefaultODataPathHandler(), routingConventions);
// below code allows endpoints to respond with either XML or JSON, depending on accept header preferences sent from client
// (default in absence of accept header is JSON)
var odataFormatters = ODataMediaTypeFormatters.Create();
config.Formatters.InsertRange(0, odataFormatters);
config.EnsureInitialized();
}
public static IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.Namespace = "Demos";
builder.ContainerName = "DefaultContainer";
builder.EntitySet<CompanyApiModel>("Companies");
builder.EntitySet<EmployeeApiModel>("Employees");
var edmModel = builder.GetEdmModel();
return edmModel;
}
}
Figured out the problem. We were overriding the EnableQuery attribute somewhere in our code and calling it EnableMappedQuery and applying it to the controller. Thus instead of having [EnableQuery(PageSize = 10)] I should have had [EnableMappedQuery(PageSize = 10)].
EnableQuery Attribute do many works,
1. it will validate the queryoption for you.
2. it will apply the queryoption for you.
3. it can add some querysettings like PageSize.
Your scenario not working is because your GetCompanies is already applied the queryoption, so when EnableQuery get the result and apply the queryoption again, it fails, it can't find the expand property, my suggestion is just return original Company and let EnableQuery do the reset of work for you, ODataQueryOption in parameter is also not needed.
If you realy do some custom work in GetCompanies and don't need EnableQuery to apply for you, you can add PageSize in ODataQuerySettings when you call method ODataQueryOptions.ApplyTo(IQueryable, ODataQuerySettings).

Resources