Accessors

Accessors occupy the controller aspect of the MVC pipeline and serve as the bridge between outside code and the mapping engine.

1
2
3
4
5
6
7
8
9
static void Main()
{
    var cust = new CustomersAccessor();
    var prod = new ProductsAccessor();

    var stats = cust.GetStats(minAge: DateTime.Now.AddMonths(-1));

    prod.ChangePrice(11, 250m);
}

They are strategically placed in your project to allow easy lookups of related Razor procedures and domain configuration from the same set of conventions as those of ASP.NET MVC.

Accessors are created by inheriting the Accessor base class and a exposing collection of public methods that internally call into either Execute<T>, Query<T> or Enumerate<T> to access their Razor commands and queries.

First we'll use the Query method to expose reading of buffered CustomerStatsView data, filtered by CustomerFilter parameters. We declare this in CustomersAccessor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
namespace Jerrystore.Accessors
{
    public class CustomersAccessor : Jerrycurl.Mvc.Accessor
    {
        public IList<CustomerStatsView> GetStats(DateTime minAge)
        {
            var filter = new CustomerFilter()
            {
                MinAge = minAge,
            };
            
            return this.Query<CustomerStatsView>(model: filter);
        }
    }
}
Alternatively you can use the Enumerate method to return unbuffered model data.

The Query (and Enumerate) methods operate on two model parameters: a generic TResult type shaping the output result and a concrete TModel shaping the input parameters.

As we will learn later, these types are central when interacting with Razor as they should match the @result and @model directive in the related .cssql file.

1
2
3
4
5
-- Queries/Customers/GetStats.cssql
@result CustomerStatsView
@model CustomerFilter

-- query body

The Execute method for executing commands works similarly, but with a single TModel to shape only the input data. As an example we'll add a ProductsAccessor to our project which will use the ProductChangePriceModel we designed earlier.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
namespace Jerrystore.Accessors
{
    public class ProductsAccessor : Jerrycurl.Mvc.Accessor
    {
        public void ChangePrice(int productId, decimal newPrice)
        {
            var model = new ProductChangePriceModel()
            {
                ProductId = productId,
                NewPrice = newPrice,
            };
            
            this.Execute(model);
        }
    }
}

In the associated ChangePrice.cssql command we will again use the @model directive to gain access to values of the ProductChangePriceModel argument.

1
2
3
4
-- Commands/Products/ChangePrice.cssql
@model ProductChangePriceModel

-- command body

The Query and Execute methods take an additional procName argument for specifying a different name for the cssql file to find, instead of the default [MethodName].cssql convention.

You can also specify options through a configure argument lambda, of which the most common scenario is excplicitly enforcing transactions through the UseTransaction and UseTransactionScope extensions.

1
2
3
4
5
6
7
8
9
10
public void ChangePrice(int productId, decimal newPrice)
{
    var model = new ProductChangePriceModel()
    {
        ProductId = productId,
        NewPrice = newPrice,
    };
            
    this.Execute(model, configure: o => o.UseTransaction());
}

Now, let's move on and learn how to write our commands and queries with Razor.