Home .NET asp.net: useful things, part three

asp.net: useful things, part three

by admin

I already gave an example of using attributes in C# and specifically in asp.net in a previous article. There we declared a simple attribute and did some logic on how to check it. This time I’d like to show you another useful attribute that’s a bit more complicated than the last one, but much more useful.
In asp.net you almost always have to deal with query string parameters. These are known to be accessed through Request, for example Request.QueryString["id"] will return the value of the id parameter or null if the parameter in the string is not defined. This is handy enough until you get tired of checking for null every time before assigning a variable. For example, if we want to initialize int id with the value of the query string id parameter, we need to write approximately the following code :

 int id = Request.QueryString[“id”] == null ? 0 : Convert.ToInt32(Request.QueryString[“id”]); 

A bit tedious, don’t you think? Also, if the task is to pre-check the initialization of a mandatory query string parameter, the work increases. In one place we check, in another place we assign. I propose to unite all the work on getting, checking and initialization of query string parameters in one place. And here again attributes will help us. Let’s declare the following attribute :

 [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = false)]sealed public class QueryStringBindingAttribute : Attribute{public string QueryStringItem{get;private set;}public bool ThrowOnNull{get;private set;}public object DefaultValue{get;private set;}public QueryStringBindingAttribute(string p_queryStringItem){SetQueryStringBindingAttribute(p_queryStringItem, false, null);}public QueryStringBindingAttribute(string p_queryStringItem, bool p_throwOnNull){SetQueryStringBindingAttribute(p_queryStringItem, p_throwOnNull, null);}public QueryStringBindingAttribute(string p_queryStringItem, bool p_throwOnNull, object p_defaultValue){SetQueryStringBindingAttribute(p_queryStringItem, p_throwOnNull, p_defaultValue);}void SetQueryStringBindingAttribute(string p_queryStringItem, bool p_throwOnNull, object p_defaultValue){if (String.IsNullOrEmpty(p_queryStringItem))throw new Exception(Properties.Settings.Default.InvalidParamsError);this.QueryStringItem = p_queryStringItem;this.ThrowOnNull = p_throwOnNull;this.DefaultValue = p_defaultValue;}} 

It says that the attribute will apply to the fields and properties of the class, will not be inherited and will only be applied once. The following properties are declared in the attribute: the actual name of the query string parameter, the pain value "should an exception be invoked" and the default value. The last property is another useful thing. Often it is necessary to set some default value in case of missing parameter value and make page construction on its basis. Property DefaultValue exactly for this purpose is defined. In the attribute defined three constructors for different situations, in the simplest case the attribute is specified to select from the query string a specific parameter, an exception is not generated and there is no default value.
To process such an attribute you need some logic. I give it below :

 public class QueryStringBinding{Page page;public QueryStringBinding(Page p_page){if (p_page == null)throw new Exception("Invalid parameter");this.page = p_page;}private void SetMemberValue(MemberInfo p_member, object value){if (p_member == null)throw new Exception("Invalid parameter");Type _memberType = p_member.GetMemberType();if (_memberType == typeof(string)){p_member.SetValue(page, value.ToString());}else if (_memberType == typeof(bool)){p_member.SetValue(page, Convert.ToBoolean(value));}else if (_memberType == typeof(int)){p_member.SetValue(page, Convert.ToInt32(value));}else if (_memberType == typeof(int?)){p_member.SetValue(page, Convert.ToInt32(value));}else if (_memberType == typeof(bool?)){p_member.SetValue(page, Convert.ToBoolean(value));}elsethrow new Exception(Properties.Settings.Default.UsupportedTypeError);}public void InitQueryStringProperties(){Type _type = page.GetType().Namespace == null ? page.GetType() : page.GetType().BaseType;MemberInfo[] _members = _type.FindMembers(MemberTypes.Field | MemberTypes.Property, BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, null);foreach (MemberInfo member in _members){bool _isDef = Attribute.IsDefined(member, typeof(QueryStringBindingAttribute));if (_isDef){Attribute _attr = Attribute.GetCustomAttribute(member, typeof(QueryStringBindingAttribute));string _requestItem = page.Request.QueryString[(_attr as QueryStringBindingAttribute).QueryStringItem];if (!String.IsNullOrEmpty(_requestItem)){SetMemberValue(member, _requestItem);}else{object _defaultValue = (_attr as QueryStringBindingAttribute).DefaultValue;if (_defaultValue != null){SetMemberValue(member, _defaultValue);}else{if ((_attr as QueryStringBindingAttribute).ThrowOnNull)throw new Exception(String.Format("The {0} parameter is not specified in the query string", (_attr as QueryStringBindingAttribute).QueryStringItem));}}}}}} 

The methods described in the first article are widely used here. As you can see from the implementation only the types string, int, bool, int? and bool? This is enough for me, but I’m sure we can and should expand the list. I don’t know, if anybody doesn’t understand anything, I’ll describe all the details of implementation, but for now here is an example, without going into details:

 [QueryStringBinding("bankid")]public int? BankId { get; set; }[QueryStringBinding("cityid", true)]public int CityId { get; set; }[QueryStringBinding("branchId", false, 0)]public int BranchId { get; set; }protected override void OnInit(EventArgs e){QueryStringBinding qsbObject = new QueryStringBinding(this);qsbObject.InitQueryStringProperties();base.OnInit(e);} 

In this example, when initializing page, class properties will be assigned to query string parameters. If there is no cityid in the query string an exception is thrown. If there is no branchId, then the property defaults to 0;
Comment : Once again I write that this implementation doesn’t pretend to be a perfect code. With your help, I would like to make it even better.
Comment2: The construct "Type _type = page.GetType().Namespace == null? page.GetType(): page.GetType().BaseType;" is introduced to solve the problem described in my second article. If anyone knows how to solve it more elegantly, please comment.

You may also like