Overriding the RetrievalQuery creation

Posts   
 
    
wtijsma
User
Posts: 252
Joined: 18-Apr-2006
# Posted on: 18-Mar-2013 16:38:34   

Hi,

Can I add another request for 4.0 runtime?

I'm looking for a way to override the creation of RetrievalQuery, which seems to be tightly coupled with the DynamicQueryEngineBase at the moment, and the CreateSelectDQ overload creating this object is not virtual unfortunately.

What I'd like to do is see if I can add some basic support for the async/await for at least selects, but the runtime framework doesn't seem to be flexible enough to override some essential parts in these areas.

Thanks!

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 18-Mar-2013 17:08:16   

async / await can be added through a wrapper on IDataAccessAdapter. It's not completely async, but it's a start.

CreateSelectDQ is virtual in adapter btw. I also don't really understand what you actually want.

Wrapper idea, from other (helpdesk) thread: Tried a little wrapper. Here's the idea:


/// <summary>
/// Wrapper class which makes it possible to use LLBLGen Pro Adapter code in an async way. 
/// </summary>
public class AsyncAdapterWrapper<TAdapter>
    where TAdapter : class, IDataAccessAdapter, new()
{
    public async Task FetchEntityCollectionAsync(IEntityCollection2 toFill, IRelationPredicateBucket filter)
    {
        await Task.Run(
            ()=>
            {
                using(var adapter = new TAdapter())
                {
                    adapter.FetchEntityCollection(toFill, filter);
                }
            });
    }
}

calling code, which is async:


public static async void FetchSalesOrderHeaderEntitiesAsync()
{
    Console.WriteLine("Entering async fetch collection method");
    var sw = new Stopwatch();
    sw.Start();
    var headers = new EntityCollection<SalesOrderHeaderEntity>();
    var wrapper = new AsyncAdapterWrapper<DataAccessAdapter>();
    await wrapper.FetchEntityCollectionAsync(headers, null);
    sw.Stop();
    Console.WriteLine("Fetched {0} LLBLGen Pro v4 entities from the DB. Took {1}ms", headers.Count, sw.ElapsedMilliseconds);
}

A call to FetchSalesOrderHeaderEntitiesAsync returns immediately, and it's executed on its own, asynchronously. The query call and materialization is still synchronous, but it won't block the main thread. One thing to notice is that I create the adapter inside the task. This is because the actual call is on a separate thread.

Frans Bouma | Lead developer LLBLGen Pro
wtijsma
User
Posts: 252
Joined: 18-Apr-2006
# Posted on: 18-Mar-2013 17:19:06   

Otis wrote:

async / await can be added through a wrapper on IDataAccessAdapter. It's not completely async, but it's a start.

CreateSelectDQ is virtual in adapter btw. I also don't really understand what you actually want.

That's a start indeed. I wanted to try and implement it all the way down on executing the DbDataReader call asynchronously.

Thanks

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 18-Mar-2013 17:20:52   

wtijsma wrote:

Otis wrote:

async / await can be added through a wrapper on IDataAccessAdapter. It's not completely async, but it's a start.

CreateSelectDQ is virtual in adapter btw. I also don't really understand what you actually want.

That's a start indeed. I wanted to try and implement it all the way down on executing the DbDataReader call asynchronously.

Thanks

that's not going to work with just IRetrievalQuery: the fetch code has to await on the Next() call on the datareader, but currently that code isn't there of course (it's .net 3.5). To be able to do async all the way, it has to be really ... all the way, sadly enough.

Frans Bouma | Lead developer LLBLGen Pro
wtijsma
User
Posts: 252
Joined: 18-Apr-2006
# Posted on: 18-Mar-2013 17:24:47   

Otis wrote:

wtijsma wrote:

Otis wrote:

async / await can be added through a wrapper on IDataAccessAdapter. It's not completely async, but it's a start.

CreateSelectDQ is virtual in adapter btw. I also don't really understand what you actually want.

That's a start indeed. I wanted to try and implement it all the way down on executing the DbDataReader call asynchronously.

Thanks

that's not going to work with just IRetrievalQuery: the fetch code has to await on the Next() call on the datareader, but currently that code isn't there of course (it's .net 3.5). To be able to do async all the way, it has to be really ... all the way, sadly enough.

Hmm too bad, thanks for the explanation

wtijsma
User
Posts: 252
Joined: 18-Apr-2006
# Posted on: 03-Apr-2013 12:13:37   

Otis wrote:

that's not going to work with just IRetrievalQuery: the fetch code has to await on the Next() call on the datareader, but currently that code isn't there of course (it's .net 3.5). To be able to do async all the way, it has to be really ... all the way, sadly enough.

So are you planning on supporting async all the way down any time soon?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 03-Apr-2013 12:37:46   

Could be, but it's not around the corner. To support async in the library we need a .NET 4.5 build. This means .NET 4.5 is the lowest .net version we are supporting in the runtime. Today that's 3.5, and I don't expect .net 4.5 to be the lowest version before the next version of .net is released wink .

So at least a couple of years away.

But people shouldn't all of a sudden think that if there's no async it's going to kill their code. It's not. First you can do async at the o/r boundary already, which gives you async calls to the orm logic, and from there it's synchronous, but at least you don't block on the calls to the orm. To fully utilize the TPL and its work-stealing scheduler, the full orm core has to be build around async which is a huge undertaking and we won't add that logic for now (see above).

Additionally, people shouldn't forget that e.g in a webapp, their code is already running in parallel: each request is executed on a different thread. This means while code INSIDE a request might block on IO, others won't. A blocking request thread might then be suspended in favor for other threads/requests. This makes good usage of the hardware available with respect to a lot of requests.

If you make code async in every detail, it will run in parallel on the hw, so 1 request might utilize all the CPUs, blocking all requests (there is a limited amount of cpus wink ). So it's a trade-off.

If you need async behavior, you can get a long way today by making the calls to the orm async. This doesn't solve the synchronous nature of the code called by itself, but will decouple the synchronous work of the orm from the caller. If the caller starts multiple queries, they will run in parallel (given multiple adapter instances of course)

Frans Bouma | Lead developer LLBLGen Pro
wtijsma
User
Posts: 252
Joined: 18-Apr-2006
# Posted on: 03-Apr-2013 14:45:24   

Otis wrote:

Could be, but it's not around the corner. To support async in the library we need a .NET 4.5 build. This means .NET 4.5 is the lowest .net version we are supporting in the runtime. Today that's 3.5, and I don't expect .net 4.5 to be the lowest version before the next version of .net is released wink .

So at least a couple of years away.

But people shouldn't all of a sudden think that if there's no async it's going to kill their code. It's not. First you can do async at the o/r boundary already, which gives you async calls to the orm logic, and from there it's synchronous, but at least you don't block on the calls to the orm. To fully utilize the TPL and its work-stealing scheduler, the full orm core has to be build around async which is a huge undertaking and we won't add that logic for now (see above).

Additionally, people shouldn't forget that e.g in a webapp, their code is already running in parallel: each request is executed on a different thread. This means while code INSIDE a request might block on IO, others won't. A blocking request thread might then be suspended in favor for other threads/requests. This makes good usage of the hardware available with respect to a lot of requests.

If you make code async in every detail, it will run in parallel on the hw, so 1 request might utilize all the CPUs, blocking all requests (there is a limited amount of cpus wink ). So it's a trade-off.

If you need async behavior, you can get a long way today by making the calls to the orm async. This doesn't solve the synchronous nature of the code called by itself, but will decouple the synchronous work of the orm from the caller. If the caller starts multiple queries, they will run in parallel (given multiple adapter instances of course)

Well doesn't it already work in 3.5 if you implement the API's asynchronously returning Task<T>?

The reason for me to want to perform DB calls asynchronously is not because of CPU bound operations but to reduce the amount of blocking threads due to slow DB calls, caused by heavy load, then hitting IIS request queue limits, but if I only make the calls to the ORM async, doesn't it still block threads from the ThreadPool waiting for the results?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 03-Apr-2013 17:21:57   

wtijsma wrote:

Otis wrote:

Could be, but it's not around the corner. To support async in the library we need a .NET 4.5 build. This means .NET 4.5 is the lowest .net version we are supporting in the runtime. Today that's 3.5, and I don't expect .net 4.5 to be the lowest version before the next version of .net is released wink .

So at least a couple of years away.

But people shouldn't all of a sudden think that if there's no async it's going to kill their code. It's not. First you can do async at the o/r boundary already, which gives you async calls to the orm logic, and from there it's synchronous, but at least you don't block on the calls to the orm. To fully utilize the TPL and its work-stealing scheduler, the full orm core has to be build around async which is a huge undertaking and we won't add that logic for now (see above).

Additionally, people shouldn't forget that e.g in a webapp, their code is already running in parallel: each request is executed on a different thread. This means while code INSIDE a request might block on IO, others won't. A blocking request thread might then be suspended in favor for other threads/requests. This makes good usage of the hardware available with respect to a lot of requests.

If you make code async in every detail, it will run in parallel on the hw, so 1 request might utilize all the CPUs, blocking all requests (there is a limited amount of cpus wink ). So it's a trade-off.

If you need async behavior, you can get a long way today by making the calls to the orm async. This doesn't solve the synchronous nature of the code called by itself, but will decouple the synchronous work of the orm from the caller. If the caller starts multiple queries, they will run in parallel (given multiple adapter instances of course)

Well doesn't it already work in 3.5 if you implement the API's asynchronously returning Task<T>?

Task<T> is .net 4.0, but async/await is .net 4.5. The code internally has to be written with the (not that friendly) .NET 4.0 api, to use async await on the outside.

The reason for me to want to perform DB calls asynchronously is not because of CPU bound operations but to reduce the amount of blocking threads due to slow DB calls, caused by heavy load, then hitting IIS request queue limits, but if I only make the calls to the ORM async, doesn't it still block threads from the ThreadPool waiting for the results?

The caller will return, so the request will be offloaded to a TPL thread. Exactly the same as when the work was broken up by the TPL scheduler. The difference is that the work offloaded to the TPL scheduler is 1 job, not a group, so that job will occupy 1 thread, but you'd always have that (it has to do work wink ).

Frans Bouma | Lead developer LLBLGen Pro