LinqToLLBL + XtraGrid + 30000 records : Very Bad performance:

Posts   
 
    
Antonio avatar
Antonio
User
Posts: 67
Joined: 09-Oct-2007
# Posted on: 23-Jul-2008 12:51:27   

LLBLGen Pro 2.6, runtime libs 2.6.8.710 XtraGrid 8.1.6

I have a XtraGrid, populated from a table with 30000 records, on a remote Sql Server database . I try to optimize it using LinqToLLBL, setting to true the grid's ServerMode property, and fill a LinqServerModeSource with the following code:


DataAccessAdapter adapter = DataAccessAdapterFactory.Adapter;
adapter.CommandTimeOut = 120; //necessary to avoid timeout on DataReader.Close
LinqMetaData linq = new LinqMetaData(adapter);
linqServerModeSource1.QueryableSource = linq.Articoli;  

it seems to retrieve the first 64 records out of 30000 to fill the first rows of the grid, but when the code reaches the method:

internal static void CleanupDataReader(IDataReader reader)
{
    if(reader != null)
    {
                if(!reader.IsClosed)
                {
                    reader.Close();
                }
                reader.Dispose();
         }
}

the reader.Close operation goes in timeout in most cases, to avoid this I set adapter.CommandTimeOut = 120, but the performances are very poor.

I precise that Articoli table is very complex, with several relations.

Do you have any suggestion to optimize the performances? Another question: why llblgen code executes a query like:

select * from Articoli

and then retrieve only the first 64 records (breaking the cycle

while(datareader.Read()) { ... }

why not use select top 64 ?

this is the output trace:


: Initial expression to process:
value(SD.LLBLGen.Pro.LinqSupportClasses.DataSource2`1[Genio.Data.Adapter.EntityClasses.ArticoliEntity]).OrderBy( => .Idarticolo).GroupBy( => 0).Select( => new [] {Convert(.Key), Convert(.Count()), Convert(.Count())})
'Genio.vshost.exe' (Managed): Loaded 'Anonymously Hosted DynamicMethods Assembly', No symbols loaded.
Method Enter: CreatePagingSelectDQ
Method Enter: CreateSelectDQ
Method Enter: CreateSelectDQ
Method Enter: CreateSubQuery
Method Enter: CreateSelectDQ
Method Enter: CreateSelectDQ
Method Enter: CreateSubQuery
Method Enter: CreateSelectDQ
Method Enter: CreateSelectDQ
Generated Sql query: 
    Query: SELECT @LO02 AS [LPFA_2] FROM [dbo].[Articoli] [LPLA_1] 
    Parameter: @LO02 : Int32. Length: 0. Precision: 0. Scale: 0. Direction: Input. Value: 0.

Method Exit: CreateSelectDQ
Method Exit: CreateSubQuery
Generated Sql query: 
    Query: SELECT DISTINCT [LPA_L3].[LPFA_2], COUNT(*) AS [LPAV_], COUNT(*) AS [LPAV_1] FROM (SELECT @LO02 AS [LPFA_2] FROM [dbo].[Articoli] [LPLA_1] ) [LPA_L3] GROUP BY [LPA_L3].[LPFA_2]
    Parameter: @LO02 : Int32. Length: 0. Precision: 0. Scale: 0. Direction: Input. Value: 0.

Method Exit: CreateSelectDQ
Method Exit: CreateSubQuery
Generated Sql query: 
    Query: SELECT @LO01 AS [LPFA_4], [LPA_L1].[LPAV_], [LPA_L1].[LPAV_1] FROM (SELECT DISTINCT [LPA_L3].[LPFA_2], COUNT(*) AS [LPAV_], COUNT(*) AS [LPAV_1] FROM (SELECT @LO02 AS [LPFA_2] FROM [dbo].[Articoli] [LPLA_1] ) [LPA_L3] GROUP BY [LPA_L3].[LPFA_2]) [LPA_L1]
    Parameter: @LO01 : Int32. Length: 0. Precision: 0. Scale: 0. Direction: Input. Value: 0.
    Parameter: @LO02 : Int32. Length: 0. Precision: 0. Scale: 0. Direction: Input. Value: 0.

Method Exit: CreateSelectDQ
Method Exit: CreatePagingSelectDQ: no paging.
: Initial expression to process:
value(SD.LLBLGen.Pro.LinqSupportClasses.DataSource2`1[Genio.Data.Adapter.EntityClasses.ArticoliEntity]).OrderBy( => .Idarticolo).Take(64)
Method Enter: CreatePagingSelectDQ
Method Enter: CreateSelectDQ
Method Enter: CreateSelectDQ
Generated Sql query: 
    Query: SELECT [LPLA_1].[IDArticolo] AS [Idarticolo], [LPLA_1].[IDCategoriaArticolo] AS [IdcategoriaArticolo], [LPLA_1].[IDClienteFornitorePrincipale] AS [IdclienteFornitorePrincipale], [LPLA_1].[IDUnitaMisura] AS [IdunitaMisura], [LPLA_1].[IDUnitaDiMisuraAcquisto] AS [IdunitaDiMisuraAcquisto], [LPLA_1].[IDUnitaDiMisuraVendita] AS [IdunitaDiMisuraVendita], [LPLA_1].[DataUltimaModifica], [LPLA_1].[IDAliquotaIva] AS [IdaliquotaIva], [LPLA_1].[GiacenzaMinima], [LPLA_1].[CodiceDistintaBase], [LPLA_1].[UltimaDataAcquisto], [LPLA_1].[DataArrivoPrevista], [LPLA_1].[PrezzoListinoUfficiale], [LPLA_1].[IDLineaProdotto] AS [IdlineaProdotto], [LPLA_1].[IDProduttore] AS [Idproduttore], [LPLA_1].[Note], [LPLA_1].[DescrizioneBreve], [LPLA_1].[DescrizioneEstesa], [LPLA_1].[CodiceFornitore], [LPLA_1].[CodiceABarre] AS [CodiceAbarre], [LPLA_1].[CodiceArticolo], [LPLA_1].[QtaPerRiordino], [LPLA_1].[GiorniRiordino], [LPLA_1].[Immagine], [LPLA_1].[NomeFileImmagine], [LPLA_1].[PesoUnitarioLordoKg], [LPLA_1].[PesoUnitarioNettoKg], [LPLA_1].[VolumeUnitario], [LPLA_1].[LarghezzaCm], [LPLA_1].[AltezzaCm], [LPLA_1].[ProfonditaCm], [LPLA_1].[PezziConfezione], [LPLA_1].[QuantitaPerCollo], [LPLA_1].[IDAspettoDeiBeniPredefinito] AS [IdaspettoDeiBeniPredefinito], [LPLA_1].[ArticoloAttivo], [LPLA_1].[ArticoloWeb], [LPLA_1].[WebDescrizione], [LPLA_1].[WebDescrizioneEstesa], [LPLA_1].[CollegamentoWeb1], [LPLA_1].[CollegamentoWeb2], [LPLA_1].[Identificatore], [LPLA_1].[IDContoAcquisto] AS [IdcontoAcquisto], [LPLA_1].[IDContoVendita] AS [IdcontoVendita], [LPLA_1].[IDContoLavorazione] AS [IdcontoLavorazione], [LPLA_1].[PercentualeProvvigione], [LPLA_1].[EscludiCalcoloProvvigione], [LPLA_1].[GestioneGiacenza], [LPLA_1].[VirtualDeleted] FROM [dbo].[Articoli] [LPLA_1]  ORDER BY [LPLA_1].[IDArticolo] ASC

Method Exit: CreateSelectDQ
Method Exit: CreatePagingSelectDQ: no paging.

thanks Antonio

p.s. using LinqServerModeSource and Linq To SQL Classes generated by Visual Studio, loading 30000 records in the xtraGrid requires 1 or max 2 seconds!

Antonio avatar
Antonio
User
Posts: 67
Joined: 09-Oct-2007
# Posted on: 23-Jul-2008 13:51:00   

I think the problem is the DataReader.Close method. From the MSDN Documentation(http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.close.aspx):

Remarks You must explicitly call the Close method when you are through using the SqlDataReader to use the associated SqlConnection for any other purpose.

The Close method fills in the values for output parameters, return values and RecordsAffected, increasing the time that it takes to close a SqlDataReader that was used to process a large or complex query. When the return values and the number of records affected by a query are not significant, the time that it takes to close the SqlDataReader can be reduced by calling the Cancel method of the associated SqlCommand object before calling the Close method.

I try with a small example:


string sql = "SELECT * from Articoli";
using (SqlConnection conn = new SqlConnection(connstring))
            {
                conn.Open();
                SqlCommand cmd = new SqlCommand(sql,conn);
                
                SqlDataReader dr = cmd.ExecuteReader();
                int i = 0;
                ArrayList list = new ArrayList();
                while (dr.Read())
                {
                    list.Add(dr[0]);
                    if (list.Count == 64)
                        break;
                }
                
                dr.Close();
            }

the dr.Close method results in Timeout Expired exception. Setting cmd.CommandTimeout =120, dr.Close() returns after about 60 seconds!

Adding cmd.Cancel() before closing DataReader, the Close method returns immediately!

Can this information be useful for optimizing LLBLgen generated code?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39866
Joined: 17-Aug-2003
# Posted on: 23-Jul-2008 15:19:50   

Thanks for the info. However, fetching 30000 rows is still going to take a lot of time, also with linq to sql. Are you sure it fetches all 30000 rows in linq to sql as well?

If you want a limited amount, just specify .Take(_number_). I know with a grid this is a bit awkward, but can't you specify paging information? As you probably don't want to see 30000 rows to show up in the grid (which will take forever to render I'm sure), I think specifying paging information is essential.

The 'cancel' trick is indeed something we don't use, but that can be very helpful, as we don't need output parameters, rows affected and other info from the datareader: when close is called, the reader is simply done. I'll see if 'cancel' is safe to be called on other datareaders besides sqlserver's.. .

(edit). Btw, 60 seconds for closing a datareader on a large set is IMHO way too much and is caused by other things than that Close() is called alone. tests here on 50,000 rows fetch with a datareader are very fast, Close() takes just a couple of ms. confused . Also a SqlDataAdapter fill uses Close() (it simply calls dispose). If you fetch a datatable with the first 100 rows using Fill on a SqldataAdapter where yuo specify 0, 100 for startrow and maxrows on that 30K table, is it still taking a lot of time?

(edit). Hmm, it indeed makes a difference if you break off a datareader loop:

string sql = "SELECT * from RandomData";
Stopwatch sw = new Stopwatch();
sw.Start();
using(SqlConnection conn = new SqlConnection(CONNSTR))
{
    conn.Open();
    SqlCommand cmd = new SqlCommand(sql, conn);

    SqlDataReader dr = cmd.ExecuteReader();
    int i = 0;
    ArrayList list = new ArrayList();
    while(dr.Read())
    {
        list.Add(dr[0]);
        if(list.Count == 64)
            break;
    }
    cmd.Cancel();
    dr.Close();

    conn.Close();
}
sw.Stop();
Console.WriteLine("Took: {0}ms", sw.ElapsedMilliseconds);

takes 800ms (table contains 50000 12 field rows with random data) over the network. Not calling cmd.Cancel takes 5000ms!

(edit). Indeed! frowning

The whole query takes 200ms at first, close takes 4800ms! .Then calling cancel right before close: query: the same, close: 10ms.

The only thing now is: is Cancel harmless?

Btw, this only occurs when the datareader is broken off before the end is reached, which is the case when paging is done on the client.

(edit). Hmm, several providers don't support this (throw exceptions), oracle's ODP.NET throws an exception when cancellation succeeds (err.... ) so it's not something which can be added globally. Casting the reader to SqlDataReader might help, as it seems that the problem mainly occurs in sqlserver.

Frans Bouma | Lead developer LLBLGen Pro
Antonio avatar
Antonio
User
Posts: 67
Joined: 09-Oct-2007
# Posted on: 23-Jul-2008 15:52:06   

Otis wrote:

Thanks for the info. However, fetching 30000 rows is still going to take a lot of time, also with linq to sql. Are you sure it fetches all 30000 rows in linq to sql as well?

If you want a limited amount, just specify .Take(_number_). I know with a grid this is a bit awkward, but can't you specify paging information? As you probably don't want to see 30000 rows to show up in the grid (which will take forever to render I'm sure), I think specifying paging information is essential.

I recommend my customers to specify filter criteria before, but this isn't mandatory. In particular cases, I need to perform operations like grouping articles by category, or calculating sums, averages, etc. I don't know if linq to sql fetches all 30000 records, but if I add a summary footer to XtraGrid it reports that the count is 30000 (exactly 33301!) When I scroll down, in server mode, it executes new queries to retrieve the "visible rows". This seems to works also with linq to llblgen, for example if i move down the vertical scrollbar the tracing output reports:



: Initial expression to process:
value(SD.LLBLGen.Pro.LinqSupportClasses.DataSource2`1[Genio.Data.Adapter.EntityClasses.ArticoliEntity]).OrderBy( => .Idarticolo).Where( => new [] {11530, 11531, 11532, 11533, 11534, 11535, 11536, 11537, 11538, 11539, 11540, 11541, 11542, 11543, 11544, 11545, 11546, 11547, 11548, 11549, 11550, 11551, 11552, 11553, 11554, 11555, 11556, 11557, 11558, 11559, 11560, 11561}.Contains(.Idarticolo))
Method Enter: CreatePagingSelectDQ
Method Enter: CreateSelectDQ
Method Enter: CreateSelectDQ
Generated Sql query: 
    Query: SELECT [LPLA_1].[IDArticolo] AS [Idarticolo], [LPLA_1].[IDCategoriaArticolo] AS [IdcategoriaArticolo], [LPLA_1].[IDClienteFornitorePrincipale] AS [IdclienteFornitorePrincipale], [LPLA_1].[IDUnitaMisura] AS [IdunitaMisura], [LPLA_1].[IDUnitaDiMisuraAcquisto] AS [IdunitaDiMisuraAcquisto], [LPLA_1].[IDUnitaDiMisuraVendita] AS [IdunitaDiMisuraVendita], [LPLA_1].[DataUltimaModifica], [LPLA_1].[IDAliquotaIva] AS [IdaliquotaIva], [LPLA_1].[GiacenzaMinima], [LPLA_1].[CodiceDistintaBase], [LPLA_1].[UltimaDataAcquisto], [LPLA_1].[DataArrivoPrevista], [LPLA_1].[PrezzoListinoUfficiale], [LPLA_1].[IDLineaProdotto] AS [IdlineaProdotto], [LPLA_1].[IDProduttore] AS [Idproduttore], [LPLA_1].[Note], [LPLA_1].[DescrizioneBreve], [LPLA_1].[DescrizioneEstesa], [LPLA_1].[CodiceFornitore], [LPLA_1].[CodiceABarre] AS [CodiceAbarre], [LPLA_1].[CodiceArticolo], [LPLA_1].[QtaPerRiordino], [LPLA_1].[GiorniRiordino], [LPLA_1].[Immagine], [LPLA_1].[NomeFileImmagine], [LPLA_1].[PesoUnitarioLordoKg], [LPLA_1].[PesoUnitarioNettoKg], [LPLA_1].[VolumeUnitario], [LPLA_1].[LarghezzaCm], [LPLA_1].[AltezzaCm], [LPLA_1].[ProfonditaCm], [LPLA_1].[PezziConfezione], [LPLA_1].[QuantitaPerCollo], [LPLA_1].[IDAspettoDeiBeniPredefinito] AS [IdaspettoDeiBeniPredefinito], [LPLA_1].[ArticoloAttivo], [LPLA_1].[ArticoloWeb], [LPLA_1].[WebDescrizione], [LPLA_1].[WebDescrizioneEstesa], [LPLA_1].[CollegamentoWeb1], [LPLA_1].[CollegamentoWeb2], [LPLA_1].[Identificatore], [LPLA_1].[IDContoAcquisto] AS [IdcontoAcquisto], [LPLA_1].[IDContoVendita] AS [IdcontoVendita], [LPLA_1].[IDContoLavorazione] AS [IdcontoLavorazione], [LPLA_1].[PercentualeProvvigione], [LPLA_1].[EscludiCalcoloProvvigione], [LPLA_1].[GestioneGiacenza], [LPLA_1].[VirtualDeleted] FROM [dbo].[Articoli] [LPLA_1]  WHERE ( ( ( ( [LPLA_1].[IDArticolo] IN (@Idarticolo1, @Idarticolo2, @Idarticolo3, @Idarticolo4, @Idarticolo5, @Idarticolo6, @Idarticolo7, @Idarticolo8, @Idarticolo9, @Idarticolo10, @Idarticolo11, @Idarticolo12, @Idarticolo13, @Idarticolo14, @Idarticolo15, @Idarticolo16, @Idarticolo17, @Idarticolo18, @Idarticolo19, @Idarticolo20, @Idarticolo21, @Idarticolo22, @Idarticolo23, @Idarticolo24, @Idarticolo25, @Idarticolo26, @Idarticolo27, @Idarticolo28, @Idarticolo29, @Idarticolo30, @Idarticolo31, @Idarticolo32))))) ORDER BY [LPLA_1].[IDArticolo] ASC
    Parameter: @Idarticolo1 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11530.
    Parameter: @Idarticolo2 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11531.
    Parameter: @Idarticolo3 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11532.
    Parameter: @Idarticolo4 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11533.
    Parameter: @Idarticolo5 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11534.
    Parameter: @Idarticolo6 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11535.
    Parameter: @Idarticolo7 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11536.
    Parameter: @Idarticolo8 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11537.
    Parameter: @Idarticolo9 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11538.
    Parameter: @Idarticolo10 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11539.
    Parameter: @Idarticolo11 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11540.
    Parameter: @Idarticolo12 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11541.
    Parameter: @Idarticolo13 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11542.
    Parameter: @Idarticolo14 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11543.
    Parameter: @Idarticolo15 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11544.
    Parameter: @Idarticolo16 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11545.
    Parameter: @Idarticolo17 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11546.
    Parameter: @Idarticolo18 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11547.
    Parameter: @Idarticolo19 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11548.
    Parameter: @Idarticolo20 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11549.
    Parameter: @Idarticolo21 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11550.
    Parameter: @Idarticolo22 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11551.
    Parameter: @Idarticolo23 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11552.
    Parameter: @Idarticolo24 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11553.
    Parameter: @Idarticolo25 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11554.
    Parameter: @Idarticolo26 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11555.
    Parameter: @Idarticolo27 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11556.
    Parameter: @Idarticolo28 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11557.
    Parameter: @Idarticolo29 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11558.
    Parameter: @Idarticolo30 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11559.
    Parameter: @Idarticolo31 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11560.
    Parameter: @Idarticolo32 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 11561.

Method Exit: CreateSelectDQ
Method Exit: CreatePagingSelectDQ: no paging.

The 'cancel' trick is indeed something we don't use, but that can be very helpful, as we don't need output parameters, rows affected and other info from the datareader: when close is called, the reader is simply done. I'll see if 'cancel' is safe to be called on other datareaders besides sqlserver's.. .

it would be a "plus" call the Command.Cancel() method, at least configuring a property or passing a parameter... let me say!

Antonio avatar
Antonio
User
Posts: 67
Joined: 09-Oct-2007
# Posted on: 23-Jul-2008 15:56:33   

Otis wrote:

(edit). Btw, 60 seconds for closing a datareader on a large set is IMHO way too much and is caused by other things than that Close() is called alone. tests here on 50,000 rows fetch with a datareader are very fast, Close() takes just a couple of ms. confused . Also a SqlDataAdapter fill uses Close() (it simply calls dispose). If you fetch a datatable with the first 100 rows using Fill on a SqldataAdapter where yuo specify 0, 100 for startrow and maxrows on that 30K table, is it still taking a lot of time?

60 seconds on a remote database server, that I access on the internet. In our LAN no problem!

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39866
Joined: 17-Aug-2003
# Posted on: 23-Jul-2008 16:02:38   

I'll add a call to cancel when the datareader is broken off AND the reader is SqlDataReader .

What the problem is I think is this: the expression tree is:

: Initial expression to process:
value(SD.LLBLGen.Pro.LinqSupportClasses.DataSource2`1[Genio.Data.Adapter.EntityClasses.ArticoliEntity]).OrderBy( => .Idarticolo).Take(64)

which contains a Take(64) call, but I see a 'Note' field, which I assume is a Text/NText field, so a distinct violating field. The DQE can't decide if this set of fields are for the complete entity and therefore won't result in duplicates, or that the query will result in duplicates, as distinct can't be emitted. One could argue that this isn't clever, and I agree, however the DQE has just the fields and some specifics, it doesn't know the fields passed in are a complete entity. For example, in the case of a compound PK containing entity, if you fetch just 1 PK field and a non PK field, you will potentially get duplicates. Specifying TOP without distinct will then cause duplicate rows (it can be) even though one of the fields is a PK.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39866
Joined: 17-Aug-2003
# Posted on: 23-Jul-2008 16:38:27   

Antonio, could you try the attached ORMSupportClasses dll to see if it helps? It will call Cancel on command if the datareader is a SqlDataReader, before Close(), otherwise it will simply call Close().

(edit) attachment removed, see post down in the thread)

Frans Bouma | Lead developer LLBLGen Pro
Antonio avatar
Antonio
User
Posts: 67
Joined: 09-Oct-2007
# Posted on: 23-Jul-2008 16:41:45   

Otis wrote:

Antonio, could you try the attached ORMSupportClasses dll to see if it helps? It will call Cancel on command if the datareader is a SqlDataReader, before Close(), otherwise it will simply call Close().

I try right now!

Antonio avatar
Antonio
User
Posts: 67
Joined: 09-Oct-2007
# Posted on: 23-Jul-2008 16:55:56   

Antonio wrote:

Otis wrote:

Antonio, could you try the attached ORMSupportClasses dll to see if it helps? It will call Cancel on command if the datareader is a SqlDataReader, before Close(), otherwise it will simply call Close().

I try right now!

it seems to work very well:

1st test: 33301 records fetched in 2.51 seconds 2st test: 33301 records fetched in 1.51 seconds 3st test: 33301 records fetched in 1.51 seconds

I hope the Cancel call does not introduce other problems!

do you think to release soon new version of runtime libraries?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39866
Joined: 17-Aug-2003
# Posted on: 23-Jul-2008 17:02:30   

I found 1 potential issue, where randomly transactions were falling apart due to this. (a blog post about this, not here).

I'll add two extra checks to the cleanup routine: 1) if the command is in a transaction, cancel isn't called 2) if the datareader.Read() method returns false, cancel isn't called either.

That should make things safe, and OK, although the MSDN doesn't describe a single issue about this, but one never knows... simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39866
Joined: 17-Aug-2003
# Posted on: 23-Jul-2008 17:09:44   

Could you try this final build as well? simple_smile (attached). If it works, I'll run the rest of the tests and push out a new build.

Frans Bouma | Lead developer LLBLGen Pro
Antonio avatar
Antonio
User
Posts: 67
Joined: 09-Oct-2007
# Posted on: 23-Jul-2008 17:22:15   

Otis wrote:

Could you try this final build as well? simple_smile (attached). If it works, I'll run the rest of the tests and push out a new build.

tested: it's ok...

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39866
Joined: 17-Aug-2003
# Posted on: 23-Jul-2008 18:05:13   

Cool thanks! simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Antonio avatar
Antonio
User
Posts: 67
Joined: 09-Oct-2007
# Posted on: 25-Jul-2008 17:19:15   

Otis wrote:

Cool thanks! simple_smile

Hi, I found that in some cases the datareader closing operation still goes in timeout!

I think this happens with more complex queries than a simple select. For example:


PrefetchPath2 pp2=new PrefetchPath2(EntityType.ArticoliEntity);
pp2.Add(ArticoliEntity.PrefetchPathProduttori);
pp2.Add(ArticoliEntity.PrefetchPathCategorieArticoli);
pp2.Add(ArticoliEntity.PrefetchPathClientiFornitori);
 pp2.Add(ArticoliEntity.PrefetchPathLineeProdotto);

var query = from art in linq.Articoli.WithPath(pp2)
                                    select new 
                                    {
                                        Idarticolo = art.Idarticolo,
                                        CodiceArticolo= art.CodiceArticolo,
                                        DescrizioneBreve=art.DescrizioneBreve,
                                        CodiceAbarre=art.CodiceAbarre,
                                        NomeProduttore = art.Produttori.NomeProduttore,
                                        Fornitore=art.ClientiFornitori.RagioneSociale,
                                        DescrizioneCategoria=art.CategorieArticoli.Descrizione,
                                        DescrizioneLineaProdotto = art.LineeProdotto.Descrizione
                                    };


Causes execution of:


SELECT [LPA_L1].[Idarticolo], [LPA_L1].[CodiceArticolo], [LPA_L1].[DescrizioneBreve], 
[LPA_L1].[CodiceAbarre], [LPA_L1].[NomeProduttore], [LPA_L1].[Fornitore], [LPA_L1].[DescrizioneCategoria], 
[LPA_L1].[DescrizioneLineaProdotto] FROM (SELECT [LPA_L3].[IDArticolo] AS [Idarticolo], [LPA_L3].[CodiceArticolo], 
[LPA_L3].[DescrizioneBreve], [LPA_L3].[CodiceABarre] AS [CodiceAbarre], [LPA_L2].[NomeProduttore], 
[LPA_L4].[RagioneSociale] AS [Fornitore], [LPA_L5].[Descrizione] AS [DescrizioneCategoria], 
[LPA_L6].[Descrizione] AS [DescrizioneLineaProdotto] 
FROM (((( [dbo].[Produttori] [LPA_L2]  
RIGHT JOIN [dbo].[Articoli] [LPA_L3]  
ON  [LPA_L2].[IDProduttore]=[LPA_L3].[IDProduttore]) 
LEFT JOIN [dbo].[ClientiFornitori] [LPA_L4]  
ON  [LPA_L4].[IDClienteFornitore]=[LPA_L3].[IDClienteFornitorePrincipale]) 
INNER JOIN [dbo].[CategorieArticoli] [LPA_L5]  
ON  [LPA_L5].[IDCategoriaArticolo]=[LPA_L3].[IDCategoriaArticolo]) 
LEFT JOIN [dbo].[LineeProdotto] [LPA_L6]  
ON  [LPA_L6].[IDLineaProdotto]=[LPA_L3].[IDLineaProdotto])) [LPA_L1] ORDER BY [LPA_L1].[Idarticolo] ASC

in this case I see that calling queryExecuted.Command.Cancel() isn't sufficient, and reader.Close() goes in timeout....

Maybe I wrote a wrong linq query? Can I optimize it?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39866
Joined: 17-Aug-2003
# Posted on: 26-Jul-2008 10:56:21   

I don't really understand why it doesn't call 'Cancel' in that query, unless it runs all the way to the end, but then Close() shouldn't take that much time. Are you fetching again just 64 items?

Frans Bouma | Lead developer LLBLGen Pro
Antonio avatar
Antonio
User
Posts: 67
Joined: 09-Oct-2007
# Posted on: 28-Jul-2008 18:19:00   

Otis wrote:

I don't really understand why it doesn't call 'Cancel' in that query, unless it runs all the way to the end, but then Close() shouldn't take that much time. Are you fetching again just 64 items?

yes, the method ProjectionUtils.FetchProjectionFromReader is called passing MaxNumberOfItemsToReturnClientSide = 64...

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39866
Joined: 17-Aug-2003
# Posted on: 28-Jul-2008 19:14:45   

Antonio wrote:

Otis wrote:

I don't really understand why it doesn't call 'Cancel' in that query, unless it runs all the way to the end, but then Close() shouldn't take that much time. Are you fetching again just 64 items?

yes, the method ProjectionUtils.FetchProjectionFromReader is called passing MaxNumberOfItemsToReturnClientSide = 64...

please provide the whole code of your testroutine, your pasted snippet above doesnt contain any Take() call for example

If there are more pages of data to read, the Cancel method is called, I don't see why it shouldn't be called in your case?

Frans Bouma | Lead developer LLBLGen Pro
Antonio avatar
Antonio
User
Posts: 67
Joined: 09-Oct-2007
# Posted on: 28-Jul-2008 19:17:43   

Otis wrote:

Antonio wrote:

Otis wrote:

I don't really understand why it doesn't call 'Cancel' in that query, unless it runs all the way to the end, but then Close() shouldn't take that much time. Are you fetching again just 64 items?

yes, the method ProjectionUtils.FetchProjectionFromReader is called passing MaxNumberOfItemsToReturnClientSide = 64...

please provide the whole code of your testroutine, your pasted snippet above doesnt contain any Take() call for example

If there are more pages of data to read, the Cancel method is called, I don't see why it shouldn't be called in your case?

the problem is that the Cancel is called, but the Close method goes in timeout

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39866
Joined: 17-Aug-2003
# Posted on: 28-Jul-2008 20:46:55   

Huh? Hmm...

I don't know why this happens, to be honest....

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39866
Joined: 17-Aug-2003
# Posted on: 30-Jul-2008 10:20:44   

Is this also occuring locally? Or just over the network? Perhaps it's network latency?

Frans Bouma | Lead developer LLBLGen Pro
Antonio avatar
Antonio
User
Posts: 67
Joined: 09-Oct-2007
# Posted on: 30-Jul-2008 12:50:04   

Otis wrote:

Is this also occuring locally? Or just over the network? Perhaps it's network latency?

the problem occurs mainly over the network.

Locally the query itself completes in just few seconds (in sql server management studio).

Over my remote server the query completes in 34 seconds (retrieving 33302 records). If I launch the query and cancel its execution immediately the time to cancel is still 34 seconds.

Antonio avatar
Antonio
User
Posts: 67
Joined: 09-Oct-2007
# Posted on: 30-Jul-2008 13:08:11   

Antonio wrote:

Otis wrote:

Is this also occuring locally? Or just over the network? Perhaps it's network latency?

the problem occurs mainly over the network.

Locally the query itself completes in just few seconds (in sql server management studio).

Over my remote server the query completes in 34 seconds (retrieving 33302 records). If I launch the query and cancel its execution immediately the time to cancel is still 34 seconds.

I discover that removing the "ORDER BY [LPA_L1].[Idarticolo] " clause from the query the DataReader.Cancel method returns immediately...

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39866
Joined: 17-Aug-2003
# Posted on: 30-Jul-2008 17:35:20   

Antonio wrote:

Antonio wrote:

Otis wrote:

Is this also occuring locally? Or just over the network? Perhaps it's network latency?

the problem occurs mainly over the network.

Locally the query itself completes in just few seconds (in sql server management studio).

Over my remote server the query completes in 34 seconds (retrieving 33302 records). If I launch the query and cancel its execution immediately the time to cancel is still 34 seconds.

I discover that removing the "ORDER BY [LPA_L1].[Idarticolo] " clause from the query the DataReader.Cancel method returns immediately...

Hmmm.... and Idarticolo is a PK ? if not, if you add an index on that field, the DB shouldn't read all rows into memory (but sort by index) and it might help. Otherwise I don't know why they have such a problem stopping a reader on a set of rows...

Frans Bouma | Lead developer LLBLGen Pro