CF2.0 application using in memory data tables or lists - best practise

S

Stephan Elsner

I am developing a time recording application that uses several lists
of data (customers, projects etc). There is not much data, it can be
held in memory and there is no need for mobile sql server. The lists
act like relational database tables and they shoul be exported as one
xml document. Now my questions:

1. My first idea was to create objects like Customers etc. and store
them in a BindingList<> to easily bind the list to a DataGrid for
display. I found out that XmlSerializer throws an exception
" ...DataList is inaccessible due to its protection level". DataList
is a class derrived from BindingList with one additional method:

class DataList<T> : System.ComponentModel.BindingList<T> where
T : DataSet
{
public T getSet(int id)
{
foreach (DataSet ds in this)
{
if (ds.Id == id)
{
return (T) ds;
}
}
return null;
}

public DataList() { }
}

"DataSet" in this case is my own DataSet whis is a base class for all
Classes containing one Row of Data (in german "Datensatz"). Dont mix
up with the ADO DataSet. No Idea why this exception occurs.

2. Is this a usable design at all? Or is it better to greate a virtual
database structure in a DataSet object? Ok, it would be easyer to
serialize (create xml) and deserialize later, but I need an oo
wrapper then, so there is much more to code...

Hou would you solve this problem?

Thanks for any hints

/Stephan
 
N

Neil Cowburn

I think that assuming there is no need to persist the data tables is a bit
of a faulty premise. How do you plan to recover should the battery run flat
or if the user soft resets the device? Both these scenario will result in
the loss of your in-memory tables unless you persist them in some way.

The reason that the XmlSerializer is failing on the your DataList class is
because its scope is marked as internal. By default, all classes without an
access modifier are internal. Set the access modifier to public. Also, the
signature for the class is overly complicated. Change this:

class DataList<T> : System.ComponentModel.BindingList<T> where T : DataSet

to this:

public class DataList : System.ComponentModel.BindingList<DataSet>

Make sure that DataList list has a public, parameterless constructor and it
should work with the XmlSerializer.

--
Neil Cowburn
Principal Partner
OpenNETCF Consulting, LLC.

Managed Code in the Embedded World

http://www.opennetcf.com/
http://www.smartdeviceframework.com/
 
S

Stephan Elsner

I think that assuming there is no need to persist the data tables is a bit
of a faulty premise. How do you plan to recover should the battery run flat
or if the user soft resets the device? Both these scenario will result in
the loss of your in-memory tables unless you persist them in some way.

No, there should be persistance, I need the xl serializer for this
(and for data export)
The reason that the XmlSerializer is failing on the your DataList class is
because its scope is marked as internal. By default, all classes without an
access modifier are internal. Set the access modifier to public. Also, the
signature for the class is overly complicated. Change this:

Thank you! I forgot 'public' in some class definitions
class DataList<T> : System.ComponentModel.BindingList<T> where T : DataSet
to this:
public class DataList : System.ComponentModel.BindingList<DataSet>

I dont need to change that, it now works like a charm (after adding a
parameterless constructor in DataList)

Thank you!
 
I

Ignacio Machin \( .NET/ C# MVP \)

Hi,

I have a similar escenario than you , an amount of data that can be
handled in memory so no need of ceSql. Not only that but the app has been
aroudn for a couple of years and it has had a couple or refiniments to load
faster the data. At least in the first installment of the framework using
XML was extremely costly.

What I do is create classes to represent my business ( Customers, Products,
etc ) , then I keep the lists as static property of the same class

This is an example of the Client class, as you see I have a Find method just
as you proposed.

public class Client
{
public int ClientID;
public string ClientName;

public static Client[] Clients= new Client[0];

public static Client Find( int clientID)
{
for(int i=0; i< Clients.Length; i++)
if( Clients.ClientID == clientID )
return Clients;
return null;
}

public static void Clear( int count)
{
Clients = new Client[count];
}
}


Now the tricky part is how to load the lists in the most efficient and fast
manner.

There are several ways of doing it, after trying several ways I found that
the fastest way was using a text file, instead of delimiting the fields with
a comma you use a non-printable char that you know for sure will not appear
inside a field, this avoid having to check if the field separator char
appear inside a field.

Another improvement was using an array for store the lists (as you can see
above), this improve the performanace over using an ArrayList for example
and makes the code cleaner avoiding the need for casting.

Loading the file is easy now, the file has this struct:
TableName
#OfItems
row1
row2
....

This is the code to load the data
public static void LoadClients(string targetfile)
{
StreamReader reader = new StreamReader( targetfile);
string currentline;
int currenttable=-1;
int index=-1;
while( (currentline = reader.ReadLine())!=null)
{
switch ( currentline)
{
case "Client" :
currenttable=0;
index = 0;
Client.Clear( Convert.ToInt32( reader.ReadLine() ));
continue;

}
string[] currentrow = currentline.Split( new char[] {(char)20});
switch ( currenttable)
{
case 0 :
try
{

Client cli = new Client();

cli.ClientID= Convert.ToInt32(currentrow[0]);
cli.ClientName = currentrow[1];
cli.ClientAddr= currentrow[2];

Client.Clients[ index++] = cli;
continue;

}
} //switch
} //while

reader.Close();


How to generate the file in the other side is your homework :)
 
G

Ginny Caughey [MVP]

Ignacio,

This is a great approach and I've used it myself. Best of all, with a
business class like this, if the requirements suddenly change later as they
did for me, it's not a lot of work to just change the load, store and search
logic to use SqlCE instead.

--
Ginny


Ignacio Machin ( .NET/ C# MVP ) said:
Hi,

I have a similar escenario than you , an amount of data that can be
handled in memory so no need of ceSql. Not only that but the app has been
aroudn for a couple of years and it has had a couple or refiniments to
load faster the data. At least in the first installment of the framework
using XML was extremely costly.

What I do is create classes to represent my business ( Customers,
Products, etc ) , then I keep the lists as static property of the same
class

This is an example of the Client class, as you see I have a Find method
just as you proposed.

public class Client
{
public int ClientID;
public string ClientName;

public static Client[] Clients= new Client[0];

public static Client Find( int clientID)
{
for(int i=0; i< Clients.Length; i++)
if( Clients.ClientID == clientID )
return Clients;
return null;
}

public static void Clear( int count)
{
Clients = new Client[count];
}
}


Now the tricky part is how to load the lists in the most efficient and
fast manner.

There are several ways of doing it, after trying several ways I found that
the fastest way was using a text file, instead of delimiting the fields
with a comma you use a non-printable char that you know for sure will not
appear inside a field, this avoid having to check if the field separator
char appear inside a field.

Another improvement was using an array for store the lists (as you can see
above), this improve the performanace over using an ArrayList for example
and makes the code cleaner avoiding the need for casting.

Loading the file is easy now, the file has this struct:
TableName
#OfItems
row1
row2
...

This is the code to load the data
public static void LoadClients(string targetfile)
{
StreamReader reader = new StreamReader( targetfile);
string currentline;
int currenttable=-1;
int index=-1;
while( (currentline = reader.ReadLine())!=null)
{
switch ( currentline)
{
case "Client" :
currenttable=0;
index = 0;
Client.Clear( Convert.ToInt32( reader.ReadLine() ));
continue;

}
string[] currentrow = currentline.Split( new char[] {(char)20});
switch ( currenttable)
{
case 0 :
try
{

Client cli = new Client();

cli.ClientID= Convert.ToInt32(currentrow[0]);
cli.ClientName = currentrow[1];
cli.ClientAddr= currentrow[2];

Client.Clients[ index++] = cli;
continue;

}
} //switch
} //while

reader.Close();


How to generate the file in the other side is your homework :)

Stephan Elsner said:
I am developing a time recording application that uses several lists
of data (customers, projects etc). There is not much data, it can be
held in memory and there is no need for mobile sql server. The lists
act like relational database tables and they shoul be exported as one
xml document. Now my questions:

1. My first idea was to create objects like Customers etc. and store
them in a BindingList<> to easily bind the list to a DataGrid for
display. I found out that XmlSerializer throws an exception
" ...DataList is inaccessible due to its protection level". DataList
is a class derrived from BindingList with one additional method:

class DataList<T> : System.ComponentModel.BindingList<T> where
T : DataSet
{
public T getSet(int id)
{
foreach (DataSet ds in this)
{
if (ds.Id == id)
{
return (T) ds;
}
}
return null;
}

public DataList() { }
}

"DataSet" in this case is my own DataSet whis is a base class for all
Classes containing one Row of Data (in german "Datensatz"). Dont mix
up with the ADO DataSet. No Idea why this exception occurs.

2. Is this a usable design at all? Or is it better to greate a virtual
database structure in a DataSet object? Ok, it would be easyer to
serialize (create xml) and deserialize later, but I need an oo
wrapper then, so there is much more to code...

Hou would you solve this problem?

Thanks for any hints

/Stephan
 
G

Guest

And using a Hashtable instead of an array would improve the speed even more,
especially if you have a fair number of items.


--
Chris Tacke - Embedded MVP
OpenNETCF Consulting
Managed Code in the Embedded World
www.opennetcf.com
--


Ignacio Machin ( .NET/ C# MVP ) said:
Hi,

I have a similar escenario than you , an amount of data that can be
handled in memory so no need of ceSql. Not only that but the app has been
aroudn for a couple of years and it has had a couple or refiniments to
load faster the data. At least in the first installment of the framework
using XML was extremely costly.

What I do is create classes to represent my business ( Customers,
Products, etc ) , then I keep the lists as static property of the same
class

This is an example of the Client class, as you see I have a Find method
just as you proposed.

public class Client
{
public int ClientID;
public string ClientName;

public static Client[] Clients= new Client[0];

public static Client Find( int clientID)
{
for(int i=0; i< Clients.Length; i++)
if( Clients.ClientID == clientID )
return Clients;
return null;
}

public static void Clear( int count)
{
Clients = new Client[count];
}
}


Now the tricky part is how to load the lists in the most efficient and
fast manner.

There are several ways of doing it, after trying several ways I found that
the fastest way was using a text file, instead of delimiting the fields
with a comma you use a non-printable char that you know for sure will not
appear inside a field, this avoid having to check if the field separator
char appear inside a field.

Another improvement was using an array for store the lists (as you can see
above), this improve the performanace over using an ArrayList for example
and makes the code cleaner avoiding the need for casting.

Loading the file is easy now, the file has this struct:
TableName
#OfItems
row1
row2
...

This is the code to load the data
public static void LoadClients(string targetfile)
{
StreamReader reader = new StreamReader( targetfile);
string currentline;
int currenttable=-1;
int index=-1;
while( (currentline = reader.ReadLine())!=null)
{
switch ( currentline)
{
case "Client" :
currenttable=0;
index = 0;
Client.Clear( Convert.ToInt32( reader.ReadLine() ));
continue;

}
string[] currentrow = currentline.Split( new char[] {(char)20});
switch ( currenttable)
{
case 0 :
try
{

Client cli = new Client();

cli.ClientID= Convert.ToInt32(currentrow[0]);
cli.ClientName = currentrow[1];
cli.ClientAddr= currentrow[2];

Client.Clients[ index++] = cli;
continue;

}
} //switch
} //while

reader.Close();


How to generate the file in the other side is your homework :)

Stephan Elsner said:
I am developing a time recording application that uses several lists
of data (customers, projects etc). There is not much data, it can be
held in memory and there is no need for mobile sql server. The lists
act like relational database tables and they shoul be exported as one
xml document. Now my questions:

1. My first idea was to create objects like Customers etc. and store
them in a BindingList<> to easily bind the list to a DataGrid for
display. I found out that XmlSerializer throws an exception
" ...DataList is inaccessible due to its protection level". DataList
is a class derrived from BindingList with one additional method:

class DataList<T> : System.ComponentModel.BindingList<T> where
T : DataSet
{
public T getSet(int id)
{
foreach (DataSet ds in this)
{
if (ds.Id == id)
{
return (T) ds;
}
}
return null;
}

public DataList() { }
}

"DataSet" in this case is my own DataSet whis is a base class for all
Classes containing one Row of Data (in german "Datensatz"). Dont mix
up with the ADO DataSet. No Idea why this exception occurs.

2. Is this a usable design at all? Or is it better to greate a virtual
database structure in a DataSet object? Ok, it would be easyer to
serialize (create xml) and deserialize later, but I need an oo
wrapper then, so there is much more to code...

Hou would you solve this problem?

Thanks for any hints

/Stephan
 
Top