Click here to Skip to main content
Click here to Skip to main content

Building an embedded database engine in C#

By , 10 Jun 2009
 
Prize winner in Competition "Best C# article of June 2009"

Introduction

This article present a standalone fully managed database/entity engine which implements fixed width record tables and BTree indexes. 

The latest source is available in CodePlex: http://dbfdotnet.codeplex.com/ 

I am welcoming anyone wanting to contribute to this project. 

Why an embedded database 

Although most of us will use a SQL Server to store and retrieve data sets.
There are several situation where an embedded database make sense.

  • When you don't have a SQL Server available
  • When you want your footprint as small as possible and can't afford SQL Express
  • When you want to manipulate or cache SQL data
  • When you need to write highly procedural data manipulation routines
  • When you want maximum speed

Features

Despite its small size DbfDotNet provides a number of features that you might find useful

  • Type safe

In DbfDotNet you manipulate classes with native field types. All data conversion plumbing is done automatically.

  • Very simple entity framework

Creating a record and accessing its propery is only what you need.

  • Very small memory footprint

Last time I checked the dbfDotNet dll was 50Kb. Other databases are 1Mb to 10Mb.

I would appreciate if someone could do some memory usage comparison (I will insert it here).

  • Fast

DbfDotNet was conceived for speed.

DbfDotNet do not use PInvoke, Threading locks, and do not implement any transaction system.
Those 3 technologies have a performance cost that it won't have to pay.

In contrast it is using TypeSafe records (without boxing/unboxing) and type safe emitted code. The code is emitted only once per table.

It has therefore I believe the potential to be the fastest embedded .Net database there is.

I would appreciate if someone could do some speed comparison (I will insert it here).

  • Very small runtime memory usage

When you use in Memory DataTable or SQL requests that return DataSets, the entire result sets is in memory.

DbfDotNet works conjointly with the garbage collector. As soon as you're finished modifying an entity the garbage collector will mark the record buffer to be saved to disk and released from memory.

Why Dbf

By default the files are compatible with dBase and can therefore be open in Excel and many other packages.

I have been asked : Why Dbf ? Dbf is an old format.

The answer is a bit long but simple.

As I said earlier DbfDotNet is designed to be as fast as possible.

In order to get the database started and get some interest I need two things:

  • A good product
  • A good user base

I know by experience that the DBF format will appeal to some of you for several reason:

  • You can easily backup DBF files (and leave index files)
  • You can check DBF content using Excel and many other tools
  • DBF is well known and simple to implement
  • It can be extended to modern types (and has been by clipper and fox pro)

Most importantly for me, implementing the .DBF rather that my own custom format has no impact on runtime speed.

How does it compare to ADO.Net, SQL, SqlLite, SharpSQL ... 

I did some speed test against another database (which I won't name) 

The results are quite encouraging. 

 Dbf.Net  ADO.Net 
Opening DbfDotNetDatabase: 185 ms
Insert 1000 individuals: 39 ms
Read individuals sequentially: 5 ms
Read individual randomly: 3 ms
Modifying individuals: 21 ms
Create DateOfBirth index: 77 ms
     Michael Simmons 22/07/1909
     Mark Adams 21/09/1909
     Charles Edwards 28/09/1909
     ... total 1000 records
Enumerate Individuals by age: 36 ms
Closing DbfDotNetDatabase: 44 ms
Opening ADO.Net Database: 459 ms
Insert 1000 individuals: 80601 ms
Read individuals sequentially: 1655 ms
Read individual randomly: 1666 ms
Modifying individuals: 75574 ms
Create DateOfBirth index: 80 ms
     Michael Simmons 22/07/1909
     Mark Adams 21/09/1909
     Charles Edwards 28/09/1909
     ... total 1000 records
Enumerate Individuals by age: 29 ms
Closing ADO.Net Database: 0 ms

In this test Dbf.Net runs nearly 400 times faster. This is quite unfair however. Dbf.Net does not have transactions and is not ACID. 

Lets not focus to much on speed but more on code differences: 

Creating a Table 

Creating the table is quite different. Dbf.Net requires a type safe record upfront to create a table.  In ADO.Net you provide a string. 

 

 Dbf.Net ADO.Net 
DbfTable<dbfdotnetindividual> mIndividuals;

void CreateIndividualTable()
{
  mIndividuals = 
    new DbfTable<dbfdotnetindividual>(
      @"individuals.dbf", 
      Encoding.ASCII, 
      DbfDotNet.DbfVersion.dBaseIV);
}


class Individual
 : DbfDotNet.DbfRecord, IIndividual
 {
  [DbfDotNet.Column(Width = 20)]
  public string FIRSTNAME;
  [DbfDotNet.Column(Width = 20)]
  public string MIDDLENAME;
  [DbfDotNet.Column(Width = 20)]
  public string LASTNAME;
  public DateTime DOB;
  [DbfDotNet.Column(Width = 20)]
  public string STATE;
 }

Connection _cnn = null;


void ITestDatabase.CreateIndividualTable()
{
  _cnn = new System.Data.Connection(
"Data Source=adoNetTest.db");
  _cnn.Open();
  using (DbCommand cmd = _cnn.CreateCommand())
 {
   cmd.CommandText = "CREATE TABLE 
     INDIVIDUAL (ID int primary key, 
     FIRSTNAME VARCHAR(20), 
     MIDDLENAME VARCHAR(20), 
     LASTNAME VARCHAR(20), 
     DOB DATE, 
     STATE VARCHAR(20))";

    cmd.ExecuteNonQuery();
  }
}

Inserting new entries in a table: 

Inserting entries differ again, in ADO you have to build a command string. In DbfDotNet you simply call the NewRecord() method and set the fields. Dbf.Net automatically uses the class you have provided to create the table. Calling the SaveChanges() is not mandatory but useful if you want your controls to refresh instantly. 

 Dbf.Net ADO.Net 
void InsertNewIndividual(
   int id, 
   string firstname,
   string middlename,
   string lastname,
   DateTime dob,
   string state)
{
  var indiv = mIndividuals.NewRecord();
  indiv.FIRSTNAME = firstname;
  indiv.MIDDLENAME = middlename;
  indiv.LASTNAME = lastname;
  indiv.DOB = dob;
  indiv.STATE = state;
  indiv.SaveChanges();
}

void InsertNewIndividual(
  int id, 
  string firstname, 
  string middlename, 
  string lastname,
  DateTime dob, 
  string state)
{
 using (DbCommand cmd =
   _cnn.CreateCommand())
 {
  cmd.CommandText = string.Format(
   "INSERT INTO INDIVIDUAL (ID,
    FIRSTNAME, MIDDLENAME, LASTNAME, 
    DOB, STATE) VALUES({0},
    '{1}', '{2}', '{3}', 
    '{4}', '{5}');",
   id, firstname, middlename,
   lastname,
   dob.ToString("yyyy-MM-dd HH:mm:ss"),
   state);
  cmd.ExecuteNonQuery();
 }
}

Getting an individual by record ID  

Getting a Individual record differs again, in ADO.Net you have to build a command string. In Dbf.Net you call a method. Also Dbf.Net automatically uses the class you have provided to create the table. Are you seeing a pattern emerging here? 

 Dbf.Net ADO.Net 
IIndividual GetIndividualById(int id)
{
  DbfDotNetIndividual result =
    mIndividuals.GetRecord(id);
    return result;
}
IIndividual GetIndividualById(int id)
{
 using (DbCommand cmd =
   _cnn.CreateCommand())
 {
  cmd.CommandText = string.Format(
    "SELECT * FROM INDIVIDUAL
     WHERE ID=" + id);
  var reader = cmd.ExecuteReader();
  try
  {
   if (reader.Read())
    return GetNewIndividual(reader);
   else return null;
  }
  finally
  {
   reader.Close();
  }
 }
}

Individual GetNewIndividual(
DbDataReader reader)
{
 var res = new Individual();
 res.ID = reader.GetInt32(0);
 res.FirstName = reader.GetString(1); 
 res.MiddleName = reader.GetString(2);
 res.LastName = reader.GetString(3);
 res.Dob = reader.GetDateTime(4);
 res.State = reader.GetString(5);
 return res;
}

 class Individual : IIndividual
 {
  public int ID { get; set; }
  public string FirstName { get; set; }
  public string MiddleName { get; set; }
  public string LastName { get; set; }
  public DateTime Dob { get; set; }
  public string State { get; set; }
 }

Saving a modified individual back to the database.

In Dbf.Net you don't have to write any code, if you don't want to wait for the garbage collector to collect your individual you can call SaveChanges

 Dbf.Net  ADO.Net 
void SaveIndividual(
  Individual individual)
{
  individual.SaveChanges();
}


void SaveIndividual(
  IIndividual individual)
{
 using (DbCommand cmd =
   _cnn.CreateCommand())
 {
  cmd.CommandText = string.Format(
    "UPDATE INDIVIDUAL
 SET DOB='{1}' WHERE ID={0};",
 individual.ID,
 individual.Dob.ToString(
   "yyyy-MM-dd HH:mm:ss"));
  cmd.ExecuteNonQuery();
 }
}

Creating an Index 

In ADO.Net you have to build a command string. In Dbf.Net you call a method. 
Despite the AddField("DOB") not looking type safe, it is internally emitting code and perfectly type safe. 
 Dbf.Net  ADO.Net 
void CreateDobIndex()
{
  var sortOrder = 
    new DbfDotNet.SortOrder<Individual>(
    /*unique*/false);
    sortOrder.AddField("DOB");
    mDobIndex = mIndividuals.GetIndex(
    "DOB.NDX", sortOrder);
}

I wish I could write sortOrder.AddField(DOB) but it wouldn't work. Anyone got an idea about this?

void CreateDobIndex()
{
 using (DbCommand cmd =
   _cnn.CreateCommand())
 {
  cmd.CommandText =
   string.Format(
   "CREATE INDEX DOB_IDX ON 
    INDIVIDUAL (DOB)");
  cmd.ExecuteNonQuery();
 }
}

Getting individuals sorted by Age 

Using the index is simple, no need to make a 'SELECT' command, just use foreach on the index.
 Dbf.Net  ADO.Net 
IEnumerable<Individual>
  IndividualsByAge()
{
  foreach (Individual indiv
    in mDobIndex)
  {
    yield return indiv;
  }
}
IEnumerable<Individual> 
  IndividualsByAge()
{
 using (DbCommand cmd =
   _cnn.CreateCommand())
 {
  cmd.CommandText = string.Format(
    "SELECT * FROM INDIVIDUAL
    ORDER BY DOB");
  var reader = cmd.ExecuteReader();
  try
  {
   while (reader.Read())
   {
    yield return 
      GetNewIndividual(reader);
   }
  }
  finally
  {
   reader.Close();
  }
 }
}

As you can see the code is generally much shorter with DbfDotNet. 

I tried to drive away from having to provide a commands in a string.  

On the contrary I tried to make it use type safe members and overall more object oriented. 

High Level Interface 

I have been asked how I compare to other SQL databases.

Again DbfDotNet is not a SQL engine.

It is rather an object persistence framework, like the Microsoft Entity Framework or NHibernate.

The difference is that it doesn't translate object manipulations into SQL requests because it speaks directly to the database layer.

I would love to write a proper Dbf to Linq interface, if you want to help me on this please volunteer. 

The difference  

Using the code 

Warning: This project is at its infancy, it has not been tested thoroughly. 

You can try it but please don't use it in a live environment. 

If you want speed however and are ready to either report or fix issues that might arrise: 

  1. Create a C# project
  2. Reference DbfDotNet.dll in your project
  3. Create a record class
  4. Write some code manipulate the records

Point 3 and 4 are expanded below.

The DbfRecord class

The DbfRecord class represent one row in your table.

You can can the column attribute to change DBF specific parameters.

    class Individual : DbfDotNet.DbfRecord
    {
        [Column(Width = 20)]        public string FIRSTNAME;
        [Column(Width = 20)]        public string MIDDLENAME;
        [Column(Width = 20)]        public string LASTNAME;
        public DateTime DOB;
        [Column(Width = 20)]        public string STATE;
    }

The system automatically chooses the DbfField most appropriate for your datatype.

The DbfTable class

In order to store your records somewhere you need to create a Table:

 individuals = new DbfTable<Individual>(
      @"individuals.dbf", 
      Encoding.ASCII, 
      DbfVersion.dBaseIV);

Note that this using a type safe template. Every record in the table are individual's.

Record Manipulation

You can add new lines in the table by using the NewRecord

    var newIndiv = individuals.NewRecord();

Then you simply use the fields in your record

    newIndiv.LASTNAME = "GANAYE";

Optionally you can make a call to SaveChanges to immediately save your changes.
If you don't the data will be saved when your individual is garbage collected.

    newIndiv.SaveChanges();

Index support

This is still very basic. First you define your sort order:

   var sortOrder = new SortOrder<Individual>(/* unique */ false); 
   sortOrder.AddField("LASTNAME");

Then you can get your index:

   mIndex = individuals.GetIndex("lastname.ndx", sortOrder);

You can then, In a type safe way, retrieve any individual from your index.

   individual = mIndex.GetRecord(rowNo);

In order to maximize speed, the index emit its own type safe code for :

  • reading the index fields from the DBF record
  • reading and writing index entries
  • comparing index entries

Inner architecture

DbfDotNet main class is the ClusteredFile

The ClusteredFile is a wrapper around stream that provide paging and caching support.

The ClusteredFile is the base class for DbfFile and NdxFile. It will also be the base class for memo files when I write them.

The ClusteredFile uses a class called QuickSerializer to serialize the record content to a byte array.

QuickSerializer parse the Record fields and generate a bit of IL code for every fields to allow reading, saving and comparison.

NdxFile implements a B+Tree index

Roadmap

My plan is to keep this library extremelly small. It is not my intention to implement any transaction or multi-threading support.

I will implement :

  • support for every DBF fields types
  • memo fields (VARCHAR type)
  • multiple indexes files (*.mdx)
  • Proper documentation
  • LINQ (in a separate dll)

If you want to help me on this project please contact me.

Points of Interest

In order to maximize speed I forced myself to not use any thread synchronization locking.

Each set of Dbf + Indexes must be called from a given thread.
In other word each dbf file and its index can be used by only one thread.

I encountered a problem though when the Garbage Collector finalize a record, this is done in the Garbage Collector thread. I did not want to lock a resource and ended up writing this code:

class Record
{
   private RecordHolder mHolder;

   ~Record()
   {
      try
      {
         ...
      }
      finally   
      {
         mHolder.RecordFinalized.Set();
      }
   }
}

Each record has a RecordHolder that store a ReadBuffer and potentially a WriteBuffer.

When the record finalize it signal the RecordHolder that the record has been finalized. This instruction is not blocking, it raises a flag that can be used in other threads.

class ClusteredFile
{
   internal virtual protected Record InternalGetRecord(UInt32 recordNo)
   {
      RecordHolder holder = null;
      if (!mRecordsByRecordNo.TryGetValue(recordNo, out holder)) {...}
      
      record = holder.mRecordWeakRef.Target;
      if (record==null)
      {
         // the object is not accessible it has finalized a while ago or is being finalized 
         if (holder.RecordFinalized.WaitOne())
         {
            //Now it has finalized we will create a new record
            holder.RecordFinalized.Reset();
            holder.Record = OnCreateNewRecord(/*isnew*/false, recordNo);
         }
      }
      return holder.Record;
   }
}

And then when the table thread try to get the record while it is disposing we use the method : holder.RecordFinalized.WaitOne() to make sure the finalization has completed first. Most of the time this method won't be blocking your DBF thread as the record has been finalized some time ago. 

History

2009 June 4th : Added samples and ADO.Net comparison
2009 June 1st : First DbfDotNet (C#) release. 

2000 May 21st : I wrote my first database engine, it is called tDbf and works on Delphi. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Pascal Ganaye
Software Developer (Senior)
United Kingdom United Kingdom
Member
I am a French programmer.
These days I spend most of my time with the .NET framework, JavaScript and html.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMulti user , authentication and Rollbackmemberunitecsoft29 Jan '10 - 0:57 
Hi Pascal , good job
 
my question is how does this databases engine handle authentication and security on the database file level , multi user accessing the same records at the same time and rollback ?
 
thanx
 
We Will Either Find A Way Or Make One .
GeneralC# Databasememberarith silva16 Jan '10 - 22:10 
Try EffiProz Database http://www.EffiProz.com?
 
EffiProz is a database written entirely in C#. comprehensive SQL support including Stored Procedures, Triggers and Functions. Ideal for embed in .Net applications. Support Silverlight 3 and .Net compact framework as well
Include Visual Studio ad-in, ADO.Net provider, Entity Framework provider, etc.
GeneralRe: C# DatabasememberThomas Krojer26 May '11 - 4:44 
effizproz is not free!
I cannot remember: What did I before google?

GeneraldBase Field TypesmemberDavid Goudie3 Jan '10 - 0:00 
Can you please give me an indication when you expect to add dBase Numeric and Date fields? I need to woprk with existing dBase files.
GeneralAbout the ColumnAttributememberlgaudouen24 Dec '09 - 4:00 
After a little test and hoping that the ColumnName property allowed a mapping of fields, I found it did not.
What's the goal of this property of the ColumnAttribute ?
GeneralCompact FrameworkmemberEber Ramirez15 Dec '09 - 6:43 
Pascal, have you plans to port your code to Compact Framework?
Your excellent engine will be very useful in WinMobile environment too.
Thanks.
 
Thumbs Up | :thumbsup:
Generalsingle client backup and restorememberkshama parashar24 Nov '09 - 18:51 
how can i will take single client backup and restore it in mysql database i want source code.
Questiontable dbfmemberMember 323381617 Aug '09 - 21:29 
FOR THE AUTHOR OF DBFDOTNET: AN OPPORTUNITY TO CREATE A TABLE RUNTIME whith the DBF COLUMNS SPECIFIED BY THE USER.
Generalstrange error? [modified]memberThomas Krojer29 Jul '09 - 3:36 
Hi,
 
first, your tool is GREAT!!!
 
creating 1 million records is < 40 sec (while in my app I´ll have about 3000-5000 records ...).
 
Indexing works fine, BUT:
 
creating a key, with counting from 0 to 999999, (leading zeroes of course), brings me an error at about 988308 ... 989838 (depending on unknown conditions)
 
How I fill my table:
for (int i = 0; i < 1000000; i++)
{
	// baseDate = baseDate.AddSeconds(1);
	ar = new AccountRecord();
	// ar.BookingKey = baseDate.ToString("yyyyMMddThhmmss");
	ar.BookingKey = i.ToString("000000000");
(the commented raises the error at 989838, the other at 988308).
 
Altough my my index was created non-unique, and my algorythm produces unique keys, the Exception "Duplicated entries in the Index file." is thrown by "NdxPage - FindInsertPos".
 
So I made further tests: using my exisiting table, I create 1000 records with the same key, after 628 .. 631 he crashes with the same error.
 
Furthermore, I hade strange errors during my tests (when closing the application window), so I changed the Record Destructor from:
 
 
~Record()
{
#if DUMP_FINALIZE
   System.Diagnostics.Trace.WriteLine("Entering Destructor " + this.GetType().Name + " #" + mHolder.mRecordNo);
#endif
 
mHolder.OnRecordDisposed(this);
}
 
to:
 
~Record()
{
#if DUMP_FINALIZE
   System.Diagnostics.Trace.WriteLine("Entering Destructor " + this.GetType().Name + " #" + mHolder.mRecordNo);
#endif
   if (mHolder != null)
   {
      mHolder.OnRecordDisposed(this);
   }
}
 
But I´m absolutly not shure about the consequences ...
 
modified on Thursday, November 5, 2009 12:32 PM

GeneralRe: strange error?memberThomas Krojer30 Jul '09 - 21:46 
After further testing, I think, this problem ONLY occurs when indexing AFTER creating the data. When creating DBF and NDX at the same time, an fill with data, everything is ok (but I cannot explan why).
Generalfluent indexesmembercs_dilo28 Jul '09 - 3:54 
I wish I could write sortOrder.AddField(DOB) but it wouldn't work. Anyone got an idea about this?
 
have you looked at FluentNHibernate? they use lambdas and then pull the propertyinfo from the expression to translate into the prop names as strings... i'm guessing you could do the same to allow for something like:
 
sortOrder.AddField<Individual>(x=>x.DOB)
or
sortOrder.AddField(x=>x.DOB)
since the SortOrder class already is generically typed
 
or something along those lines... i forget exactly how the pull the propinfo from the lambda expression, but it was relatively simple and straightforward... it used reflection, but since u'd only take the perf hit on creating the index, you shouldn't notice it at all at runtime
Questionsome something to comfirmmemberairsoon24 Jul '09 - 4:21 
Now I want to write a file to the database,Can I?If I can,what should I do? Smile | :)
Thank you.
GeneralQuestions regarding the accuracy of your ADO.NET Database Performance test as reported in the articlememberDrABELL21 Jul '09 - 17:58 
Dear Pascal,
 
I hope this message finds you well.
 
I have some serious doubts in regards to ADO.NET Database performance metrics as reported in your article:
the actual ADO.NET DB performance (IMHO) would be about several order of magnitude better than you have stated in your comparative speed test.
 
Specifically:
 
1). You have stated that it took 80601 ms (more than a minute!) for ADO.NET to just insert 1000 rows containing several fields. In fact, this operation, even when running on Access DB, should take less than 10 sec if you programmed/implemented ADO.NET/Database objects properly.
 
2). In regards to reading individual DB rows (randomly/sequantially) using ADO.NET you have specified about 1.6 sec per row, which is mind boggling: the actual nubmers should be several order of magnitude faster! As an example, just take a look at my web project: www.jobrica.com, which utilizes standard ADO.NET Database classes (intrinsic VS2008 Data-aware objects). The table on the first (home) page reads 200 rows (Job Agencies in Tri-State area) within 1...2 sec INCLUDING NETWORK TRAFFIC and GUI!
 
Another table on the same site retrives US occupational statistics DB table, containing more than 800 rows within 3..5 sec, also INCLUDING NETWORK TRAFFIC and GUI (re: http://www.jobrica.com/_RESOURCES/OccupationalStat.aspx).
 
CONCLUSION: Though I did not comment on your own development, but your comparative analysis regarding ADO.NET Database performance seems to be a way off the reality check, namely: properly implemented standard ADO.NET operations running on the properly designed standard SQL server (or Access) Databases are much faster (about several order of magnitude) than specified in test results in your article.
 
Please clarify.
 
Kind Regards,
 
Alex
 
Alexander Bell, NY, USA
GeneralRe: Questions regarding the accuracy of your ADO.NET Database Performance test as reported in the articlememberPascal Ganaye21 Jul '09 - 23:22 
To be fair I think you're right, there are ways to get the ADO.Net to run faster.
 
I found it terribly slow too.
I know that you could probably speed this by using parameterized command or store procedures when available.
I took my speed samples straight from SQLite own testing suite.
Using a straight "INSERT INTO ... " command is I believe the shortest way to write it in ADO and therefore to save time it will often be the way that programmer will choose.
 
The source of the speed test program is available there:
http://dbfdotnet.codeplex.com/sourcecontrol/changeset/view/23744?projectName=dbfdotnet[^]
It is called 'SqlLiteTest'.
GeneralRe: Questions regarding the accuracy of your ADO.NET Database Performance test as reported in the articlememberEber Ramirez24 Aug '09 - 16:50 
Pascal, I did some test with SQLite.Net and 1000 inserts takes around 90 ms including some work, processing strings, before inserts. I think it is not fair at all to use those numbers. I am in agreement with DrAbel.
I am not using something special to this, check it out:
//Add data to DB
string line;
int textProcessed = 0;
int wordCountForTimer = 0;
 
DateTime beforeProcess = DateTime.Now;
try
{
while ((line = text.ReadLine()) != null)
{
textProcessed += line.Length;
int percentage = (int)(textProcessed * 100 / letterSize);
 
if (line != string.Empty && line.Contains('('))
{
string[] field = new string[3];
field[0] = string.Empty;
field[1] = string.Empty;
field[2] = string.Empty;
 
char[] letters = line.ToCharArray();
 
int fieldNumber = 0;
 
foreach (char character in letters)
{
 
if (fieldNumber == 0 && character == '(')
{
fieldNumber++;
}
 
field[fieldNumber] += character.ToString();
 
if (fieldNumber == 1 && character == ')')
{
fieldNumber++;
}
}
 
if (field[0].Length < 30)
{
dataReturn[0] = wordCount.ToString();
dataReturn[1] = page.ToString();
worker.ReportProgress(percentage, dataReturn);
 
wordCount++;

sqCommand.Parameters[0].Value = field[0];
sqCommand.Parameters[1].Value = field[1];
sqCommand.Parameters[2].Value = field[2];
 
sqCommand.CommandText =
@"INSERT INTO [dict] ([word], [type], [mean]) " +
"VALUES (?, ?, ?)";
sqCommand.ExecuteNonQuery();
wordCountForTimer++;
}
}
}
 
dataReturn[0] = wordCount.ToString();
dataReturn[1] = page.ToString();
worker.ReportProgress(100, dataReturn);
sqTrans.Commit();
 
DateTime afterProcess = DateTime.Now;
TimeSpan dif = afterProcess - beforeProcess;
MessageBox.Show(dif.ToString() + " ms for " + wordCountForTimer.ToString() + " words");

 
Anyways, your program is excellent. I used to make programs in Clipper.
 
Sniff | :^)
GeneralRe: Questions regarding the accuracy of your ADO.NET Database Performance test as reported in the articlememberDrABELL14 Sep '09 - 5:06 
Hi everybody,
 
I would agree with Eber.
 
Thanks and regards,
 
Alex (DrABELL)
QuestionWhat about complex relationships?memberSchmuli20 Jul '09 - 22:51 
Hi!
 
I read the article and all the comments, and this looks very interesting, and like others have mentioned, this is something that everyone needs occasionally (instead of SQL Server ... ).
I see you mention Entity Framework in the article, so I wanted to know if will support complex relationships between tables. Now, I read where you said Joins could slow things down, but one of the main things that I like about EF (and Linq to SQL as well), is the Typed relationships, meaning my Individual can have a Property like Address, which can be a different table and therefore a different object.
This makes obtaining the Address much simpler, i.e. myIndividual.Address.City, or in the case of one-to-many relationships, it means that I can select multiple related entries very simply, without even using (in EF for example) the DataContext.
There is also the question of having a Property whose type is an Enumeration, how would that map into the DBF types available?
 
Thanks for a great article and great idea.
AnswerRe: What about complex relationships?memberPascal Ganaye20 Jul '09 - 23:31 
> I wanted to know if will support complex relationships between tables.
There are no build-in relationship support at this stage.
You would have to write something like:
get_address(client.addresss).city
It would be nice to have this built in I agree.
 
I agree with you that client.address.city would be better.
The difficulty is to have it fetch the address content only when required.
 
I am thinking of moving away from fields and build a wrapper using properties. This would require to use Mono.Cecil or LINFU though and make the entire library a lot bigger. So I am not decided yet.
 
> There is also the question of having a Property whose type is an
> Enumeration, how would that map into the DBF types available?
 
I would use either DBF 'N' field and store a number or a 'C' field and store the first letter.
Generalquery optimizermemberLeblanc Meneses20 Jul '09 - 14:43 
so how are you parsing/interpreting sql statements?
Depending what hasn't been done I may be able to help in this area.
 

thanks
GeneralRe: query optimizermemberPascal Ganaye20 Jul '09 - 22:41 
This database does not parse or interpret SQL.
At this stage it implements Length Record tables and B+Tree indexes.
A SQL engine could be built on top of it though yes, but I think a LINQ interface would be more beneficial.
GeneralRe: query optimizermembertim12428 Jul '09 - 13:09 
I dont understand why people want to use SQL with it. The great thing about it is that you dont need to use SQL!
GeneralPerformancememberMember 202295120 Jul '09 - 12:17 
Thank you for sharing.

Why not use the memory? It can enhance performance.
 
Best Regards,
Pedro Lopes
GeneralRe: PerformancememberPascal Ganaye20 Jul '09 - 22:43 
DataTable are using memory and are pretty good.
This library is useful when your tables are too big for memory or when you want the memory footprint to be low.
GeneralPerformance for large tablesmemberkawfeeEsim20 Jul '09 - 9:10 
How does this database perform for tables that have two-hundred thousand records? Are the insertions quick for inserting 20k records at a time?
Is the data access quick to lookup 80k records at a time?
 

Thanks.
GeneralRe: Performance for large tablesmemberPascal Ganaye20 Jul '09 - 22:44 
Insertion is really only 'an append at the end' so yes it is very fast.
It can cope with 20K without flinching much faster than ADO.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 10 Jun 2009
Article Copyright 2009 by Pascal Ganaye
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid