c# - DataSet within class is not being refreshed asynchronously? -


tl;dr

sorry if question title misleading; it's work in progress try root of issue.

i making .single( row => ... ... ) call against .asenumerable( ) extension of datatable throwing exception. looking table, there 2 rows present impossible because, looking @ source table (the 1 on data base datatable should populated), seeing 1 of these rows.

in effort narrow down problem, assigning primary key table, generating impossible exception telling me primary key exists within table, should not case table should have been just created.

why happening?

did read:

i'd hoped in course of forming question have come upon solution in 1 of "talking colleague/cardboard cutout aha" moments; not much.

there quite bit of code going on here, , explain best able trying accomplish (i apologize, not know precisely of proper terminology trying describe).

i working on project component act intermediary between online data base , desktop application. portability primary concern, making component event driven.

it case there many things going on must run concurrently; such, have endeavoured implement component asynchronously (async , await).

the program depends on static class (which have constructed) contains number of datatables.

before post code, should initial suspicion multiple asynchronous tasks running in parallel are... "crashing each other" somehow. not know if case certain, though.

i start believe source of problem , work way down through stack. attempt in depth-first manner sake of brevity. more can added later if necessary.

before trace, want post method primary suspect:

/// <summary> /// asynchronously read tables sqllibasync instance. /// </summary> public async task readtables( ) {     this.ds = new dataset( );     using ( mysqlconnection msqcon = new mysqlconnection( this.connectionstring ) ) {         await msqcon.openasync( );         try {             foreach ( mysqldataadapter adapter in this.adapters ) {                 adapter.selectcommand.connection = msqcon;                 await adapter.fillasync( this.ds,                       adapter.tablemappings.cast<datatablemapping>()                          .first( )                          .sourcetable                  ); //in fact, troubles originating .cast<>() call?             }         } catch ( exception ex ) {             ex.report( );         }         await msqcon.closeasync( );     }     if ( this.ds.tables.count == 0 )         await this.readtables( ); } 

this method has given me quite bit of trouble , has gone through several iterations. in past receiving exception informing me connection attempt had been made while datareader accessing connection, or something, believe because using multiple instances of class work asynchronously, , in improper fashion such work (basically, multiple connections single computer same database), went route seems have dealt issue, getting new issue. believe that, reason, parallel asynchronous tasks , method not getting along, lack know why.

the start of tree methods use, in context of having problems, here:

public static async task start( ) {     /*i have omitted code appears working properly. #1*/     bool         pending = true;     /*another code omission. #2*/                while ( session.instance.status != sessionstatus.finalized && session.userid != 0 ) {         list<task> tlist = new list<task>();         tlist.add( session.instance.playercheck( ) );         switch ( session.instance.status ) {             case sessionstatus.pending:                 if ( pending ) {                     pending = false;                     tlist.add( session.onsessionreport( ) );                 }                 break;               }         /*another omission #3*/         await task.whenall( tlist );     }     /*more omitted code*/ } 

we start session.instance

private static session _instance; // private static reference session.  /// <summary> /// static reference instance of session. /// </summary> public static session instance { { return session._instance; } } 

next have thestatus property of session class:

private sessionstatus status { //retrieve fresh session status.     {         switch ( this.freshrow.field<string>( "status" ) ) {             /*translates incoming string enum. #4*/         }     } } 

the status property references freshrow property:

private datarow freshrow { //retrieves datarow fresh session information can extracted. (status, buzzedinuser, etc).     {         if ( this.sessionid == 0 )             throw new objectdisposedexception( "session", "illegal attempt access disposed session object." );         return qatitables.gamesessions.fresh.asenumerable( ).single( row => row.field<uint32>( "game_session_id" ).equals( this.sessionid ) );     }     /*session.sessionid simple property getter , private setter.*/ } 

from here have qatitables.gamesessions property:

public static sqltableasync gamesessions {     {         return qatitables.tables[1] ?? ( qatitables.tables[1] = new sqltableasync(             "qandatimeserver.txt",             "game_sessions",             "created_by = @param_val_0", //we have grab of these every time because row status may change mid-game , lose it.             new object[ ] { qatitables.users.fresh.asenumerable( ).single( ).field<uint32>( "user_id" ) } ) );     }     /*the referenced qatitables.tables[] array of sqltableasync objects.*/ } 

this property makes use of sqltableasync class:

/// <summary> /// select * [table] [filter] /// </summary> /// <param name="serverfile">ftp file containing database connection data.</param> /// <param name="tablename">table retrieve.</param> /// <param name="filter">query filter</param> /// <param name="parameters">parameters on filter.</param> public sqltableasync( string serverfile, string tablename, string filter, object[ ] parameters ) {     this.tablelib = new sqllibasync( "qandatimeserver.txt" );     try { this.tablelib.gettable( tablename, filter, parameters ).wait( ); } catch ( exception e ) {         e.report( );         /*report custom extension method exceptions. #5*/     }     this.tablename = tablename; } 

from within constructor have sqllibasync class:

/// <summary> /// initialize new sqllib /// </summary> /// <param name="databaseconnection">text file read database connection parameters on file server</param> public sqllibasync( string databaseconnection ) { this.connectionfile = databaseconnection; }  /// <summary> /// set ftp file read connection data. /// </summary> private string connectionfile {     set {          /*this populates local variables information file read           directly ftp server. such, have omitted code           functions should, , contains sensitive           bits of information.*/     } } 

we've reached end of our first branch! hooray!

going up, our next referenced method sqllibasync.gettable( ) method:

/// <summary> /// select * [table] [filter] /// </summary> /// <param name="table">table name</param> /// <param name="filter">query filter</param> /// <param name="parameters">object of parameters populate filter.</param> public async task gettable( string table, string filter, object[ ] parameters ) {     await this.gettables( new string[ ] { table }, new string[ ] { filter }, new object[ ][ ] { parameters } ); } 

this method makes reference sqllibasync.gettables( ... ) method:

/// <summary> /// return multiple tables data set. /// </summary> /// <param name="tables">tables query</param> public async task gettables( string[ ] tables, string[ ] filters, object[ ][ ] parameters ) {     this.adapters = new mysqldataadapter[tables.length];     int filterindex;     object[ ] parameterset = null;     string query = null, filter = null;     foreach ( string table in tables ) {         filterindex = tables.tolist( ).indexof( table );         filter = filters[filterindex];         parameterset = parameters[filterindex];         query = "select * " + table + " ";         if ( string.isnullorempty( filter ) )             query += "1;";         else             query += filter + ";";         mysqldataadapter adapter = new mysqldataadapter( new mysqlcommand( query ) { commandtype = commandtype.text } );         if ( parameterset != null )             ( int x = 0; x < parameterset.length; x++ )                 adapter.selectcommand.parameters.addwithvalue( "@param_val_" + x, parameterset[x] );         adapter.tablemappings.add( table, table );         this.adapters[tables.tolist( ).indexof( table )] = adapter;     }     await this.readtables( ); } 

this method makes use of first described sqllibasync.readtables method. brings end of our second branch.

we rescind qatitables.gamesessions property reference qatitables.users property:

public static sqltableasync users {     {         return qatitables.tables[0] ?? ( qatitables.tables[0] = new sqltableasync(             "qandatimeserver.txt",             "users",             "last_ip_address = @param_val_0 , role in (@param_val_1, @param_val_2) , is_login = @param_val_3",             new object[ ] { methods.ipaddress, "admin", "entertainer", 1 } ) );     } } 

this property references static methods.ipaddress property, omit feel context in used defines sufficiently.

this property makes use of sqltablesasync class, it's methods , properties, described previously.

this property brings end of branch.

the next branch fresh property of sqltableasync class, referenced in freshrow property of session class:

/// <summary> /// fresh table. /// </summary> public datatable fresh { {     try {         this.tablelib.readtables( ).wait( );         return this.tablelib.tables[this.tablename];     } catch ( exception ex ) {         ex.report( );         return null;     } } } 

this makes reference tablelib variable; local sqltableasync class object. makes reference readtables method of class (described previously), tables property of class:

/// <summary> /// sqllibs table set. /// </summary> public datatablecollection tables { { return this.ds.tables; } } 

this property makes reference class ds variable, dataset described in previous methods.

and finally, we've reached end of our first major branch, status property of session class, , of it's related classes, properties, methods , functions, brings our next major branch: userid property of session class:

private static uint32 userid { // static logged in userid     {         ienumerable<datarow> users = qatitables.users.fresh.asenumerable( ); //to avoid multiple unnecessary queries.         return users.count( ) == 1 ? users.single( ).field<uint32>( "user_id" ) : 0;     } } 

fortunately, branch ends references users property of qatitables class, has been described in detail above. ends start of our initial while loop in start method above.

the next branch playercheck method of session class. before meat of method, method designed run frequent checks against database , inform program when players join , leave game. method may considered second suspect cause of problem.

private async task playercheck( ) {     list<task> tlist = new list<task>( );     ienumerable<player> candidates = ( await qatitables.players.freshasync( ) ).asenumerable(         ).select<datarow, player>( row => new player( row.field<uint32>( "participant_id" ), this.sessionid ) );     candidates.where( p =>         !( playerstatus.blocked | playerstatus.kicked | playerstatus.quit | playerstatus.logoff ).hasflag( p.status ) &&         !this._players.contains( p ) ).tolist( ).foreach( p => {             this._players.add( p );             tlist.add( session.onplayerjoined( p ) );         } );     candidates.where( p =>         ( playerstatus.blocked | playerstatus.kicked | playerstatus.quit | playerstatus.logoff ).hasflag( p.status ) &&         this._players.contains( p ) ).tolist( ).foreach( p => {             this._players.remove( p );             tlist.add( session.onplayerleft( p ) );         } );     await task.whenall( tlist ); } 

within method have first referenced players property of qatitables class:

public static sqltableasync players {     {         try {             return qatitables.tables[7] ?? ( qatitables.tables[7] = new sqltableasync(                 "qandatimeserver.txt",                 "session_participants",                 "session_id = @param_val_0",                 new object[ ] { qatitables.gamesessions.fresh.asenumerable( ).where( row =>                 !qatitables.gameendedfilter.contains( row.field<string>( "status" ) )                 ).single( ).field<uint32>( "game_session_id" ) } ) );         } catch ( exception ex ) {             ex.report( );             return null;         }     } } 

this method references sqltableasync class familiar. going up, see session.playercheck method referencing fresasync( ) function of sqltableasync class:

/// <summary> /// fresh table asynchronously. /// </summary> /// <returns>refreshed table</returns> public async task<datatable> freshasync( ) {     await this.tablelib.readtables( );     return this.tablelib.tables[this.tablename]; } 

this method identical fresh property, has been tagged async keyword.

moving session.playercheck method, see method selecting qatitables.players rows collection of player class objects using session_participant_id of data row , sessionid of session class:

/// <summary> /// create new player object. /// </summary> /// <param name="playerid">player id</param> /// <param name="sessionid">id of session player in.</param> public player( uint32 playerid, uint32 sessionid ) {     this.playerid = playerid;     this.sessionid = sessionid; } 

going up, part filters our candidates ienumerable<player> on criteria present , active within current game (not blocked, kicked, etc) , not presently accounted within our current session. makes use of player.status property...

    /// <summary>     /// player status.     /// </summary>     public playerstatus status {         {             switch ( this.freshrow.field<string>( "status" ) ) {                 /*converts string appropriate player status*/             }         }     } 

...which makes use of player.freshrow property...

private datarow freshrow {//retrieve fresh row data may extracted.     {         if ( this.playerid == 0 || this.sessionid == 0 )             throw new objectdisposedexception( "player", "illegal attempt access disposed player object." );         try {             return qatitables.players.fresh.asenumerable( ).single( row =>                 row.field<uint32>( "participant_id" ).equals( this.playerid ) &&                 row.field<uint32>( "session_id" ).equals( this.sessionid ) );         } catch ( exception ex ) {             ex.report( );             return null;         }     } } 

... exception coming!!! single( row => ... ... ) throwing exception, saying there exists 2 rows within data table match provided criteria!!! made it!!! property makes reference fresh property of players property of qatitables class, sqltableasync class object should familiar.

phew!!!

for sake of being complete, referenced _players value of session class list of player class objects, , have referenced static session.onplayerjoined( player ) method:

private static async task onplayerjoined( player p ) {     if ( session._playerjoined != null )         await task.run( ( ) => session._playerjoined( session.instance, p ) ); } 

this method invokes event if exists in asynchronous fashion. nothing fancy going on here, @ least, don't think so.

thus ends first filter of candidates ienumerable. next filter similar; filters players on criteria have been removed game (blocked, kicked, etc), , presently accounted for, is, within list of players session.

one other piece of information: player class implements iequatable<player> interface in following fashion:

public class player : idisposable, iequatable<player> {     /*code omitted*/     /// <summary>     /// check if player equivalent player.     /// </summary>     /// <param name="other">player compare player.</param>     /// <returns>playerid.equals(other.playerid)</returns>     public bool equals( player other ) { return this.playerid.equals( other.playerid ); } } 

this takes end of branch within playercheck method, , start method. other branch left in tree session.onsessionreport( ) method which, intents , purposes, identical onplayerjoined , onplayerleft methods: calls event handler asynchronously, if event handler not null.

now we've traversed tree, problem having (as described above in brief) this: when calling player.status property, single( row => ... ... ) method within freshrow property of class throwing exception, telling me there multiple rows match criteria on filtering. absurd because filtering on primary key of data table row. yet, somehow, when managed @ table, sure enough, there two, yes two, rows matched provided criteria. yet, when looked @ table on data base table gets information, find one.

why happening?

edit

in effort narrow down issue, going implement sqltableasync , sqllibasync classes support adding primary keys. should result in exception when datatable populated multiple rows containing same primary key.

to end have changed fresh property , freshasync methods of sqltableasync class following:

public datatable fresh { {     try {         this.tablelib.readtables( ).wait( );         if (this.primarykeycolumn != null)             this.tablelib.tables[tablename].constraints.add( "primarykey",             this.primarykeycolumn.select<string, datacolumn>( columnname => this.tablelib.tables[tablename].columns[columnname] ).toarray( ),             true );         return this.tablelib.tables[this.tablename];     } catch ( exception ex ) {         ex.report( );         return null;     } } }  public async task<datatable> freshasync( ) {     await this.tablelib.readtables( );     if ( this.primarykeycolumn != null )         this.tablelib.tables[tablename].constraints.add( "primarykey",             this.primarykeycolumn.select<string, datacolumn>( columnname => this.tablelib.tables[tablename].columns[columnname] ).toarray( ),             true );     return this.tablelib.tables[this.tablename]; } 

also; constructor of sqltableasync class takes string[ ] column names on primary key should based , assigns value local primarykeycolumn string[ ] variable. getting new exception on qatitables.players.fresh property telling me there exists constraint within table matches constraint.

what

the fresh method should dumping dataset ds within sqllibasync class when calls readtables method of class, meaning no keys should exist within tables of data set has just been created because readtables method being called just prior trying assign primary key.

i need coffee...

the issue single throw exception if number other 1 row found. use firstordefault , null check see msdn link here +1 extensive set of source code, nice post!

edit: after review 1 of single statements out of place, check out

public static sqltableasync players { get; } 

it looks single need take in filter lambda or make first.


Comments

Popular posts from this blog

java - Andrioid studio start fail: Fatal error initializing 'null' -

android - Gradle sync Error:Configuration with name 'default' not found -

StringGrid issue in Delphi XE8 firemonkey mobile app -