This project is read-only.
If you need more documentation than this, I will have not met my design goals. Let me know if you believe something can be done in a more clear and concise manner. For a loosely structured document of things to come for this library, visit the Development Roadmap

Quick Reference

For a more complete list of functionality with examples go to the Quick Reference. The concepts described below should give you a good idea of what this library provides.

Features

  • Efficiently read objects from the database (Speeds equivalent to Dapper).
  • Read multiple objects from the database in one trip to the DB
  • Read a single value
  • Read an array of values
  • Self tracking entities
  • Merge changes between to entities
  • Will work with Linq generated entities
  • Write all object updates, insertions, and deletes in one trip to the DB
  • Get detailed events for any inserts, updates, and deletes performed with the same accessor
  • Easily set up a self-managed, in-memory cache of entities that can be loaded and expired however you like
  • Create filtered / sorted "views" of that cache that automatically incorporate changes to the cache
  • "Views" are read only and they implement INotifyCollectionChanged
  • "Views" take advantage of the weak event pattern to prevent memory leaks

Optional features

These options consist of those moments where I may have taken leave of my decency in search for something that should be, but is not obtainable in any strictly legitimate manner.
  • Passing multiple values as a single parameter. Convenience at a cost. For Sql Server developers, this will result in more execution plans being created for your query. This is due to the direct string replacement of the query parameter for a string literal replacement of values.
// using the 'AccessorExtension.AllowEnumerableParameters' option
var thing1s = m_Accessor.ReadEntities<Thing1>(
    "SELECT * FROM [Thing1] WHERE [Id] IN (@0)", 
    new[] { 1L, 2L, 3L }
    );
  • Safe DateTime boundaries. For Sql Server developers, passing a DateTime or DateTimeOffset value that is outside of the Sql Server datetime boundries, your parameter value will have the Sql Server datetime min or max substituted for it.
// using the 'AccessorExtension.LimitDateParameters' option
var thing1s = m_Accessor.ReadEntities<Thing1>(
    "SELECT * FROM [Thing1] WHERE [Created] > @0", 
    DateTime.MinValue
    );

The Core Objects

The Record (IDbRecord)

The record is the lowest base class for all data objects that can be read out of the database via the IDbAccessor. Records are read-only, but contain most of the functionality the DrivenDb library depends on.

[DrivenTable(Schema = "dbo", Name = "TableA")]
public class TableA : DbRecord<TableA>
{
    [DrivenColumn(IsDbGenerated = false, IsPrimaryKey = false, Name = "MyString")]
    public string MyString
    {
        get;
        set;
    }
}

The Entity (IDbEntity)

The entity is the read/writable version of DbRecord. Some added functionality comes with the DbEntity, like the tracking of changes and the entity's state (New, Current, Modified, Deleted)

[DrivenTable(Schema = null, Name = "TableA")]
protected class TableA : DbEntity<TableA>, INotifyPropertyChanged
{
    private string m_MyString;

    [DrivenColumn(IsDbGenerated = false, IsPrimaryKey = false, Name = "MyString")]
    public string MyString
    {
        get { return m_MyString; }
        set
        {
            m_MyString = value;
            PropertyChanged(this, new PropertyChangedEventArgs("MyString"));
        }
    }
}

The Accessor (IDbAccessor, IDbAccessorSlim)

The accessor is how all entities are read and written to and from the database. There are many convenience methods for reading and two methods for writing. It is also an event publisher of all database changes that occur through itself. The accessor is designed to be widely shared if not a singleton within your application. One accessor is not aware of changes happening to another accessor instance. Not all databases support multiple sql statements in one command. The "slim" version of the accessor eliminates methods from the default accessor that are based on this concept.

var accessor = DrivenFactory.CreateAccessor(
     DbAccessorType.MsSql, 
     AccessorExtension.All,
     () => new SqlConnection(CSTRING)
     );

var tableAs = accessor.ReadEntities<TableA>("SELECT * FROM TableA");

The Index (IDbIndex)

The index is a convenience structure designed as a dictionary specifically for entities. The benefit of this structure is mostly for cleaner code. Though there are index management features that are unique as well. Specifically, you can't just add or replace an entity in the dictionary. You must choose how this will happen. For example, you can either merge a new entity with an existing one, fail if one exists, replace the existing one, or ignore the new entity if it already exists.

var entity = accessor.ReadIdentity<TableA, int>(1);
var index = DrivenFactory.CreateIndex<int, TableA>((a) => a.Id);
	
index.AddOrFail(entity);
//or index.AddOrIgnore(entity);
//or index.AddOrMerge(entity);
//or index.AddOrReplace(entity);

The View (IDbView, INotifyingDbView)

The view gives you an ordered and/or filtered subset of a cache or index. Views monitor changes in their source cache or index and are always current via the INotifyCollectionChanged interface. Indexes and caches take advantage of the weak event pattern to prevent memory leaks, so create as many views as you want, as often as you like. INotifyingDbView broadcasts it's own INotifyCollectionChanged events and is ideal for WPF utilization. It is also possible to create views of views.

// a view can also be created from an IDbCache instance
var index = DrivenFactory.CreateIndex<int, MyTable>((t) => t.MyIdentity);
var view = index.ViewAs((a) => a.Value > 100);

The Cache (IDbCache)

The cache is a self managed in-memory cache of entities stored in an index. When you construct the cache, you tell it how to load an initial set of entities, retrieve entities it doesn't have, how to add those entities to the index, and how to get rid of entities when you want to.

public class CoreA : IDbCacheCore<int, TableA, DateTime>
{
    private readonly Timer m_Timer;
    private readonly IDbAccessor m_Accessor;
	
    public MyCore(IDbAccessor accessor)
    {
        m_Accessor = accessor;
        m_Timer = new Timer((o) => FlushEntities(this, null), null, 0, 600000);
    }
	
    public int ExtractKey(TableA item)
    {
        return item.MyIdentity;
    }
	
    public TableA RetrieveEntity(int id)
    {
        return m_Accessor.ReadIdentity<TableA, int>(id);
    }
	
    public IEnumerable<TableA> RetrieveInitialEntities()
    {
        return m_Accessor.ReadEntities<TableA>(
            "SELECT * FROM TableA WHERE Created > GETDATE()"
            );
    }

    public bool CacheEntity(TableA entity, DateTime info, out IndexAdditionMethod method)
    {
        method = IndexAdditionMethod.Replace;
        return true;
    }
	
    public bool FlushEntity(TableA entity, DateTime info)
    {		
        return (info - DateTime.Now).Minutes > 10;
    }

    public event EventHandler FlushEntities;

    public DateTime CreateInfo(TableA entity)
    {
        return DateTime.Now;
    }
}

var core = new CoreA(<an accessor>);
var cache = DrivenFactory.CreateCache<int, TableA, DateTime>(core);

Last edited Aug 11, 2014 at 3:09 AM by TBone512, version 21

Comments

No comments yet.