Language preference:

Phase 1 / Customizing the Data Access Objects

Let's focus on the Data Access Objects. These classes contain the methods for CRUD ("Create, Read, Update, Delete") actions on a specific Entity class.

BLD has created your Data Access Object from the base class PeterBlum.DES.EntityDAO.BaseEntityDAO <EntityType>. BaseEntityDAO and its subclasses are called BLD DataAccessObjects. BLD DataAccessObjects provide advanced features such as caching, paging, and work with the User Interface's filtering controls.

As you look through the files that were generated, you will see classes like CategoryDAO and ProductDAO. These are your Data Access Objects.

BLD DataAccessObjects already know how to interact with LINQ to SQL, Entity Framework or whichever data acccess technology that you have chosen, but do so in a generic way. For example, their Update method does not use stored procedures, as those are specific to your application. Now is the time to customize their Update, Insert, and Delete methods to conform to your business logic.

In the code file Order.cs shown in the Source Code Browser, you will see sample methods. They assign a value to the Order's CustomerID property before letting the ancestor to the rest. In your own code, you could call a stored procedure, prepare the Entity instance differently or write data differently from the base class. However, you do not write validation code here. Validation has already occurred prior to calling these methods.

protected override void Update(LINQtoSQLEntityDAO<Order>.L2SChangeEntityActionWrapper pWrapper)
{
   Order order = pWrapper.GetEntity();
   if (order.Customer != null)
      order.CustomerID = order.Customer.CustomerID;
   base.Update(pWrapper);
}
protected override void Insert(LINQtoSQLEntityDAO<Order>.L2SChangeEntityActionWrapper pWrapper)
{
   Order order = pWrapper.GetEntity();
   if (order.Customer != null)
      order.CustomerID = order.Customer.CustomerID;
   base.Insert(pWrapper);
}

Your Data Access Object should provide methods that return a list of Entities matching a query. In BLD, we call these Select methods.

BLD DataAccessObjects already have some generic Select methods.

  • Select() – Follows the filters and sort keys passed in. Handles any query developed by the consumer of your Data Access Object.
  • SelectAll() – Returns all Entity objects. Follows the sort keys passed in.
  • SelectOne() – Pass in the unique ID of a record and it returns that record.
  • MultiFieldTextSearch() – Show all records that match text to find and the list of DataFields with their FilterAttribute.InMultiFieldSearch set to true.

The Select() and SelectOne() methods are automatically used by BLD's Page Templates, so its important that they conform to your database. SelectOne() as designed only takes a single primary key. The Order Details table has two primary keys. So the Order_Details Entity class implements a custom SelectOne() method as shown in the code file Order Details.cs in the Source Code Browser.

[DataObjectMethod(DataObjectMethodType.Select)]
[SelectArgs(Filters=false, SortExpression=false, Paging=false)]
[SelectMethodParameter(ActualName="orderID", AliasNames="Order",
   Description="The primary key to a record in the Orders table.")]
[SelectMethodParameter(ActualName="productID", AliasNames="Product",
   Description="The primary key to a record in the Products table.")]
public IEnumerable<Order_Detail> SelectOne(int orderID, int productID, SelectArgs args)
{
   args.AddEntityFilter(new CompareToValueEntityFilter("OrderID", DES.ConditionOperator.Equal, orderID));
   args.AddEntityFilter(new CompareToValueEntityFilter("ProductID", DES.ConditionOperator.Equal, productID));

   return ExecuteSelectCommand(args);
}

Your application is likely to need other queries for which you can add Select methods. You don't have to add every case. The UI is able to create filter objects, called EntityFilters, which they pass to the Select methods within the SelectArgs parameter, allowing them to narrow the scope even further.

The SelectArgs parameter is found on any of these Select methods, and provides all kinds of information for your method to consume (or ignore). That includes the EntityFilters, the Sort expression, if paging is supported, and caching rules. It also contains tools for your Select method to communicate its own requirements with the BLD query engine within the Data Access Object.

Below are a few examples of Select methods to support Product entities. You will find these in the ProductDAO class of Product.cs file to the right.

// Returns products between a price range. Supports filters
// and sorting passed in through the SelectArgs.
// Uses EntityFilters to add a filter to the query. 
// (There are many ways to adjust the query, such as using LINQ
// and the PeterBlum.DES.DAO.EntityDAO.WhereClause class which
// lets you construct a SQL-like statement.)
[DataObjectMethod(DataObjectMethodType.Select, false)]
[SelectMethodParameter(ActualName="startPrice", AliasNames="StartPrice")]
[SelectMethodParameter(ActualName="endPrice", AliasNames="EndPrice")]
[Description("All Products whose UnitPrice is within these parameters.")]
[SelectArgs(Filters=true, SortExpression=true, Paging=true)]
public IEnumerable<Product> SelectPriceRange(
  decimal startPrice, decimal endPrice, SelectArgs selectArgs)
{
   if (endPrice == 0)
      endPrice = 999999999.99M;
   selectArgs.EntityFilters.Add(
      new RangeEntityFilter("UnitPrice", startPrice, endPrice));
 
   return ExecuteSelectCommand(selectArgs);
}

// Uses a stored procedure called Ten Most Expensive Products that has
// already been added to the NorthWindDataContext class as a method
// named Ten_Most_Expensive_Products(). It takes no parameters.
[DataObjectMethod(DataObjectMethodType.Select, false)]
[SelectMethodParameter(ActualName="startPrice", AliasNames="StartPrice")]
[SelectMethodParameter(ActualName="endPrice", AliasNames="EndPrice")]
[Description("All Products whose UnitPrice is within these parameters.")]
[SelectArgs(Filters=true, SortExpression=true, Paging=true)]
public IEnumerable<Product> TopTen(SelectArgs selectArgs)
{
   NorthWindDataContext vDataContext = new NorthWindDataContext();
   var vProducts = vDataContext.Ten_Most_Expensive_Products();
   return ExecuteSelectCommand(vProducts, selectArgs, "TenMostExpensive");
}

In the next topic, you will explore the EntityFilters that are used in your Select methods and many other places.



Open the Source Code Browser (C# only)
Source Code Browser
 
/* ------------------------------------------------
 * Describes the Entity class for: Order
 * Classes:
 *    Order - Entity class. Edit it for validation and to customize metadata at runtime
 *    OrderMetadata - Entity Metadata class. It contains the DataAnnotations.
 *    OrderDAO - BLD DataAccessObject format version of a Data Access Object.
 *    
 * Requires .net 4.0 and these references:
 *    System.ComponentModel.DataAnnotations
 *    PeterBlum.DES
 *    PeterBlum.DES.DataAnnotations
 * Generated: 7/8/2011 4:19:41 PM
 * ------------------------------------------------*/
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using PeterBlum.DES.DAO.Attributes;
using PeterBlum.DES.DAO.EntityDAO;
// Some members of this namespace have identical names to those in System.ComponentModel.DataAnnotations
// making it easy to switch from one to the other by adding the "DESDA." prefix
using DESDA = PeterBlum.DES.DataAnnotations;

namespace PeterBlum.WithDataAnnotations
{
   // --- ENTITY CLASS --------------------------------------------------
   /// <summary>
   /// Entity class. Edit it for validation and to customize metadata at runtime
   /// </summary>
   [EntityDAOType(typeof(OrderDAO))]
   [MetadataType(typeof(OrderMetadata))]
   public partial class Order : DESDA.ICustomizeDataField
   {
   }  // class Order

   // --- ENTITY METADATA --------------------------------------------------
   /// <summary>
   /// Entity Metadata class.
   /// Companion to the Order Entity class that contains the DataAnnotations
   /// on properties with the same names as those in the actual Entity class.
   /// These properties do not require their types to match those in the Entity class.
   /// An Entity Metadata class allows the Entity class to be regenerated without
   /// overwriting DataAnnotations.
   /// </summary>
   public class OrderMetadata
   {
   }  // class OrderMetadata

   // --- BLD DATAACCESSOBJECT  --------------------------------------------------
   /// <summary>
   /// BLD DataAccessObject class for the Order Entity class.
   /// It provides CRUD actions. The parent class already has default
   /// methods for Update(), Insert(), Delete() and several queries.
   /// </summary>
   /// <remarks>
   /// <para>For documentation, see the BLD DataAccessObject section of the Business Logic User's Guide.</para>
   /// </remarks>
   [TableName("Orders")]
   public class OrderDAO : LINQtoSQLEntityDAO<Order>
   {
      public OrderDAO() : base(typeof(NorthWindDataContext)) { }
      public OrderDAO(object pDataContext) : base(pDataContext) { }

      protected override void Update(LINQtoSQLEntityDAO<Order>.L2SChangeEntityActionWrapper pWrapper)
      {
   // NOTE: This is an example that doesn't really work. This process is handled 
   // by LinqToSql in the SET selector of Order.Customer.
   // Here order.Customer is always null.
         Order order = pWrapper.GetEntity();
         if (order.Customer != null)
            order.CustomerID = order.Customer.CustomerID;
         base.Update(pWrapper);
      }

      protected override void Insert(LINQtoSQLEntityDAO<Order>.L2SChangeEntityActionWrapper pWrapper)
      {
   // NOTE: This is an example that doesn't really work. This process is handled 
   // by LinqToSql in the SET selector of Order.Customer.
   // Here order.Customer is always null.
         Order order = pWrapper.GetEntity();
         if (order.Customer != null)
            order.CustomerID = order.Customer.CustomerID;
         base.Insert(pWrapper);
      }


   }  // class OrderDAO

}