Model Definition

By default, Teaq makes a best effort to map simple DTO classes with public properties when performing object materialization from a data reader. However, column names don't always match .NET property names. Often, database naming standards are not compatible with .NET naming standards. To bridge this gap, you define a model that expresses type to table mappings, and property to column mappings.

The entry point for building up a data model is the DataConfiguration class, as follows:

  var model = DataConfiguration.BuildModel(
                builder => 
                {
                    builder.Entity<SomeType>()
                    .Column(e => e.SomeProperty)
                    .HasMapping("MyColumn")
                    .IsNVarChar(50);
                });

In the above example, the BuildModel method is invoked, and Action<IDataModelBuilder> is passed as an argument. This action is a callback which will allow the Teaq framework the ability to configure the specified types you may define in your model.

To specify a configuration for your type, use the Entity<T> method shown above. This method will allow you to chain configuration for your type and its properties using the Column<TProperty> method. You can put as many statements in this callback as needed, and you can call any helper methods, ...whatever, this is just another lambda. Just because you can, don't put extraneous code here, and do keep in mind configuration will only execute once.

By way of model configuration, you can specify computed columns such as identity and time stamps. You can also designate a column as a concurrency token. For example:

 var model = DataConfiguration.BuildModel(
                builder =>
                {
                    builder.Entity<SomeType>()
                    .ConcurrencyToken(e => e.MyByteArray)
                    .Column(e => e.MyId).IsIdentity().IsKey().IsOfType(SqlDbType.Int);
                });

The above example specifies the "MyByteArray" property represents a concurrency token, which is expected to be a SQL timestamp data type, by convention. In addition, the property "MyId" is defined as an identity, a key value (which will exclude this column from update) and of type integer.

NOTE: You can only declare an entity of a given type once in the callback action. If you violate this rule, you will get an ugly exception telling you there is a duplicate key. Embrace chaining everything important about a particular entity type in a single fluent statement (for now).

Any API Teaq exposes to generate a query or handle object materialization will allow passing of an IDataModel instance. This is always optional. You can configure as many or as few data models for different scenarios, as needed. In most cases models represent design time knowledge and can be configured once per app domain and used as singletons. If needed, models can also be conditionally built at run-time and this isn't overly expensive to do.

Teaq does not allow definition of navigation properties (AKA object hierarchies). Instead, you are expected to stitch these together however you must from tabular data sets of strongly typed .NET DTOs. You can put together a query batch that returns multiple result sets as a means to this end, with minimal database round trips. This is discussed in the Query Execution and Query Batching section.

Query Building

Also, what about writing nice strongly typed, IDE friendly expressive query statements and having Teaq create a query? You can do this using the query builder fluent interface. To do this, use a model that you have already defined:

 var command = model.Query<MyEntity>()
                .BuildSelectCommand(a => a.Id == 6);


The above snippet forms a simple select statement that will select from the table mapped to "MyEntity" (by default: dbo.MyEntity), and return all rows that have an Id column value of 6. Simple enough, but this won't do anything until the resulting QueryCommand instance is executed, either directly or as part of a batch. To execute a ready QueryCommand instance, see Query Execution and Query Batching.

How about an update statement?

// Model, defined as a singleton somewhere, inline here for the example:
var model = DataConfiguration.BuildModel(
    config =>
    {
        config.Entity<Customer>()
            .Column(c => c.CustomerId).IsKey()
            .Exclude(c => c.CustomerKey);
    });
    
var cust = new Customer { CustomerId = 5 };

// Now build the query and get a query command instance to do something:
var qc = model.Query<Customer>()
    .BuildUpdate(cust, c => c.CustomerId ==cust.CustomerId)
    .ToCommand();

The above example will configure an update statement that filters for a matching customer ID in the Customer table (by default), and update all "non-key", not excluded properties based on property to column name matching, or explicit column mapping defined in the model.

Insert statements are similar except you "BuildInsert". Delete? Same pattern. Be sure to include a predicate.

What if you have everything set up to match by convention and don't need to declare any sort of mapping? As of version 1.3 or later, you can use an ambient, empty default model:

var qc = DataConfiguration.Default.Query<Customer>()
    .BuildUpdate(cust, c => c.CustomerId ==cust.CustomerId)
    .ToCommand();


You can also do as follows, if so inclined to keep GC busy, or if you are <1.3:

var defaultModel = DataConfiguration.BuildModel(x => {});

Again, prefer a singletons as there is rarely a need to redefine any model at run time.

Last edited May 29, 2016 at 6:04 PM by bkjuice, version 33