The Telerik RadGrid is a great grid component for .NET developers and I can't recommend it, or the rest of the ASP.NET controls from Telerik, highly enough. The control has a vast amount of options, from drag and drop columns, filtering, templating and hierarchical databinding. Go and check out the demo's on their demo website. This post will cover setting up the data access for true data paging as well as hooking into the appropriate server side events
The grid can handle 300,000 rows with in-memory databinding, according to the Telerik website. When you potentially reach this upper limit and beyond you want to look at true data paging of results SQL Server side, which is made even more easier in SQL Server 2005 and later using Common Table Expressions. This is the most efficient way of data paging within SQL by supplying a page number and a page size to work out the record set to retrieve, improving website response time and saving memory allocation. See an example below:
CREATE PROCEDURE [dbo].[Web_Accounts_Get]
@CompanyID int,
@PageSize int = 10,
@PageNumber int = 1
AS
BEGIN
SET NOCOUNT ON;
DECLARE @PageRecordStart INT
DECLARE @PageRecordEnd INT
SET @PageRecordStart = ((@PageNumber - 1 ) * @PageSize) + 1
SET @PageRecordEnd = @PageNumber * @PageSize
;WITH Accounts_Page AS
(
SELECT Row_Number() OVER (ORDER BY [AccountName]) AS RowNo
,[Id]
,[CompanyID]
,[AccountID]
,[AccountName]
,[PostCode]
,[AccountTypeID]
,[DateUpdated]
,[DateAdded]
FROM [dbo].[tblAccount] WITH (NOLOCK)
WHERE [CompanyID] = @CompanyID
)
SELECT ,[Id]
,[CompanyID]
,[AccountID]
,[AccountName]
,[PostCode]
,[AccountTypeID]
,[DateUpdated]
,[DateAdded]
FROM Accounts_Page
WHERE RowNo BETWEEN @PageRecordStart and @PageRecordEnd
ORDER BY RowNo ASC;
END
The Telerik RadGrid exposes an OnNeedDataSource event that you can hook into to provide data access to bind to the grid. Here I use the RadGrid's CurrentPageIndex and PageSize properties to pass to my data access for our SQL CTE.
protected void rdgrdAccounts_OnNeedDataSource(object sender, GridNeedDataSourceEventArgs e)
{
int startRowIndex = rdgrdAccounts.CurrentPageIndex + 1;
int maximumRows = rdgrdAccounts.PageSize;
rdgrdAccounts.VirtualItemCount = Account.CountForFilters(CompanyId, filters);
rdgrdAccounts.DataSource = Account.GetAccounts(CompanyId, maximumRows, startRowIndex);
}
In the next post I will cover allowing multiple checkbox selection of rows when using true data paging.
Share or Bookmark this post…
In Part 1 and Part 2 I covered how I went about building an authorization framework using Rhino Security. I am now going to cover how to integrate this into your domain model (business objects) and how we can use dynamic discovery for permission registration and database persistence.
In essence what I am saying here is that given the scenario whereby we have a domain object (lets use that of a Company business object, for example) we could state what available permissions that object could have. This post will therefore cover:
- Defining permissions at an object level
- The ability to register those permissions in the database
Defining Object Permissions
Having considered various implementations, I finally decided to go with an attribute based approach. The attribute requires the operation that you want access control over and to what level of granularity. For example, given the flexibility of the Rhino Security implementation we can use the operation convention of /BUSINESSOBJECT/OPERATION or /BUSINESSOBJECT/METHOD/OPERATION OR /BUSINESSOBJECT/PROPERTY/OPERATION.
In reality this would result in the following examples:
- /Company/Add
- /Company/Delete
- /User/UserName/Set
As you can see this attribute (ACL) used below, it takes as a parameter, the operation and an enum that states what type of permission it is.
namespace MyApp.Core
{
[ACL("/User/Add", "Restrict the ability to add new users", ACLType.ObjectLevel)]
public class User : Entity, IUser
{
private IAuthorizationRepository _authRepo;
protected User(IAuthorizationRepository authRepo)
{
_authRepo = authRepo;
}
public User()
{
_authRepo = ResolveType.Of<IAuthorizationRepository>();
}
[Length(6, 512)]
[NotNullNotEmpty]
public virtual string UserName { get; set; }
[Length(6, 512)]
[NotNullNotEmpty]
public virtual string Password { get; set; }
Dynamic Permissions Discovery
Having created an attribute to apply the required information to an object we can use reflection to discover all of our permissions. Those permissions that have not been registered in the database can then be registered and any that no longer exist can be marked accordingly. The way that I implemented this was run this discovery in the Application_Start event of the Global.asax. Therefore whenever we start our application this process takes place and the overhead of some pretty heavy reflection is less of an issue.
This process, although sounding quote complex, is actually very very simple. I call a static Discover method that uses relflection to reflect the assembly, find those objects that the ACLAttribute has been applied to, checks whether that object is registered and if not then registers it.
public static void Discover(Castle.Windsor.IWindsorContainer Container)
{
Assembly asm = Assembly.Load("MyApp.Core");
Type[] types = asm.GetTypes();
Type securityPermission = asm.GetType("MyApp.Core.Security.ACLAttribute", false);
PropertyInfo operation = securityPermission.GetProperty("Operation");
RegisterAssemblyTypes(Container, types, securityPermission, operation);
}
private static void RegisterAssemblyTypes(Castle.Windsor.IWindsorContainer Container, Type[] types, Type securityPermission, PropertyInfo operation)
{
foreach (Type t in types)
{
object[] objs = t.GetCustomAttributes(securityPermission, false);
foreach (object o in objs)
{
if (!Registered(operation.GetValue(o, null).ToString(), Container))
{
Register(operation.GetValue(o, null).ToString(), Container);
}
}
}
}
This implementation works very well. It involves minimal work work the developer. When a new permission is required it simply means adding an attribute to the appropriate business object. This permission will then be automatically registered and will be visible within the permissions section of the application, allowing the administrator to associate a particular user with that permission to allow or deny the operation.
In the next post I will detail the how I implemented this using some nice jQuery iPhone checkboxes on top of the standard ASP.NET checkbox control.
Share or Bookmark this post…