The best approach I've found here is by using transactions to isolate activities in the database, making sure you roll back a transaction at the end of your test run, and never commit.
In this way, your database is always left in a known state. Some ORMs use a pattern of storing the transaction or connection object in a ThreadStatic field or a named slot on the thread, so it's possible to tie the transaction to the thread. You could also do this yourself if your are calling your way into the database manually (i.e. DSQL/custom DAL).
Issues start to arise with this approach when you have multiple tests running in parallel, each one manipulating interdependent areas of the database. This can cause deadlocks, so the tests can hang. If you mark all tests that write data to the database using ExclusivelyUses("Database"), and all tests that only read data from the database as InclusivelyUses("Database") you can significantly increase the number of tests that can run in parallel.
Another option is to have a seperate database for each processing thread (or rather, execution process). This has its own advantages by removing the parallelisation constraints but the infrastructure for setting up the databases can then add to the length of the test run.