The Inverted Software DataBlock Quick Start Tutorial
Author
Gal RatnerThe Inverted Software DateBlock has been recently enhanced, so I thought I would take the opportunity to write a short quick start tutorial that would help you get started with incorporating it into your application:
Getting objects
CRUDHelper.GetObject<Category>(() => new Category(), "GetCategory", mainConnectionString, new SqlParameter("@categoryCode", SqlDbType.VarChar, 200) { Value = "myValue" });
Getting collections
CRUDHelper.GetObjectList<Category>(() => new Category(), "GetCategories", mainConnectionString);
For a paged list use:
CRUDHelper.GetObjectList<Category>(() => new Category(), 0, 10, "GetCategories", mainConnectionString, out virtualTotal);
Getting parent child collections
If you use a single stored procedure to retrieve parent / child objects you have two methods of populating a list of parents and children:
You can use an inner join query:
SELECT p.ProductID AS Product_ProductID, c.CategoryID AS Product_CategoryID, c.CategoryID AS Category_CategoryID, c.CategoryName AS Category_CategoryName, p.ProductName AS Product_ProductName FROM Product p INNER JOIN ProductCategory pc ON p.ProductID = pc.ProductID INNER JOIN Category c WITH(NOLOCK) ON pc.CategoryID = c.CategoryID
with the following method:
CRUDHelper.GetEagerLoadedObjectListFromInnerJoinQuery<Category, Product>(() => new Category(), () => new Product(), "GetCategoriesWithProducts", mainConnectionString);
Or multiple result sets:
-- First select SELECT CategoryID, CategoryName FROM Category WITH(NOLOCK) -- second select SELECT p.ProductID, c.CategoryID, p.ProductName FROM Product p INNER JOIN ProductCategory pc ON p.ProductID = pc.ProductID INNER JOIN Category c WITH(NOLOCK) ON pc.CategoryID = c.CategoryID
with the following:
CRUDHelper.GetEagerLoadedObjectListFromMultipleResults<Category, Product>(() => new Category(), () => new Product(), "GetCategoriesWithProducts", mainConnectionString);
Getting collections of immutable objects
You can get a list of integers, strings or decimals.
CRUDHelper.GetIntList("GetCategoriesWithProducts", "CategoryID", mainConnectionString);
CRUDHelper.GetStringList("GetCategoriesWithProducts", "CategoryName", mainConnectionString);
Creating records from objects
CRUDHelper.AddObject<Category>(category, "AddCategory", mainConnectionString);
Updating objects
CRUDHelper.UpdateObject<Category>(category, "UpdateCutegory", mainConnectionString);
Deleting records
CRUDHelper.DeleteObject<Category>(category, "DeleteCategory", mainConnectionString);
Working with output parameters
public static List<T> GetObjectList<T>(Func<T> generator, int pageIndex, int rowsPerPage, string sprocName, string stringConnection, out int virtualTotal, params SqlParameter[] commandParameters) { List<T> objectList = new List<T>(); SqlParameter[] paramArray = new SqlParameter[]{ new SqlParameter("@PageIndex", SqlDbType.Int){ Value = pageIndex}, new SqlParameter("@PageSize", SqlDbType.Int){ Value = rowsPerPage}, new SqlParameter("@TotalRecords", SqlDbType.Int){ Direction = ParameterDirection.ReturnValue } }; if (commandParameters != null) paramArray = paramArray.Concat(commandParameters).ToArray(); SqlCommand cmd = SqlHelper.CommandPool.GetObject(); using (SqlConnection conn = new SqlConnection(stringConnection)) { try { SqlHelper.PrepareCommand(cmd, conn, null, CommandType.StoredProcedure, sprocName, paramArray); using (SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection)) { PropertyInfo[] props = ObjectHelper.GetDataObjectInfo<T>().Properties; List<string> columnList = ObjectHelper.GetColumnNames(rdr, sprocName); T newobject; while (rdr.Read()) { newobject = generator(); ObjectHelper.LoadAs<T>(rdr, newobject, props, columnList, sprocName); objectList.Add(newobject); } } virtualTotal = Convert.ToInt32(paramArray.Where(p => p.ParameterName == "@TotalRecords").First().Value); cmd.Parameters.Clear(); } catch (Exception e) { throw new DataBlockException(String.Format("Error Getting object list {0}. Stored Procedure: {1}", typeof(T).FullName, sprocName), e); } finally { SqlHelper.CommandPool.PutObject(cmd); } } return objectList; }
Transactions
There are two ways of using transactions with the DataBlock:
SqlTransaction
public static int AddCategoryWithSqlTransaction() { int newRecordID = 0; using (SqlConnection connection = new SqlConnection(mainConnectionString)) { connection.Open(); SqlTransaction transaction = connection.BeginTransaction("SampleTransaction"); SqlParameter[] paramArray = new SqlParameter[]{ new SqlParameter("@ParentCategoryID", SqlDbType.Int){ Value = DBNull.Value}, new SqlParameter("@CategoryName", SqlDbType.VarChar, 50) { Value = "test1" }, new SqlParameter("@Active", SqlDbType.Bit) { Value = true } }; try { newRecordID = Convert.ToInt32(SqlHelper.ExecuteScalar(connection, transaction, CommandType.StoredProcedure, "AddCategory", paramArray)); transaction.Commit(); } catch (Exception ex) { transaction.Rollback(); } } return newRecordID; }
And ScopeTransaction
public static int AddCategoryWithTransactionScope() { int newRecordID = 0; using (TransactionScope scope = new TransactionScope()) { SqlParameter[] paramArray = new SqlParameter[]{ new SqlParameter("@ParentCategoryID", SqlDbType.Int){ Value = DBNull.Value}, new SqlParameter("@CategoryName", SqlDbType.VarChar, 50) { Value = "test2" }, new SqlParameter("@Active", SqlDbType.Bit) { Value = true } }; try { newRecordID = Convert.ToInt32(SqlHelper.ExecuteScalar(mainConnectionString, CommandType.StoredProcedure, "AddCategory", paramArray)); scope.Complete(); } catch (Exception ex) { throw ex; } } return newRecordID; }
Object attributes
The DataBlock uses property level attributes to perform mapping on CRUD operations as well as parent / child objects.
Here is an example of typical attribute mapping:
public class Category { [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)] [CrudField(UsedFor = CrudFieldType.Delete | CrudFieldType.Read | CrudFieldType.Update)] public int CategoryID { get; set; } public int? ParentCategoryID { get; set; } public string CategoryName { get; set; } [CrudField(UsedFor = CrudFieldType.DontUse)] public List<Product> CategoryProducts { get; set; } }
public class Product { [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)] [CrudField(UsedFor = CrudFieldType.Delete | CrudFieldType.Read | CrudFieldType.Update)] public int ProductID { get; set; } public string ProductName { get; set; } [ForeignKey("CategoryID")] [CrudField(UsedFor = CrudFieldType.DontUse)] public int CategoryID { get; set; } }
The DataBlock is available as a NuGet package and the source is provided on GitHub.