Showing posts with label Attribute. Show all posts
Showing posts with label Attribute. Show all posts

Sunday, 11 December 2022

Generic Attributes in C#11

 

In C# 11 comes with .Net 7, Attribute is enhanced to use with generic type. This feature provides a convenient way for attributes to avoid System.Type constructor parameter (the old way).

Attributes are metadata extensions that give additional information to the compiler about the elements in the program code at runtime. Attributes are used to impose conditions or to increase the efficiency of a piece of code.

Being a programmer I love this. Here I will try to explain to you how to define Attributes with generic types. To understand the enhancement, let's see how we used to do the Attributes with type in C# 10 or before.

For example, we will create an Asp.Net core web API project template with the target framework net7.0 so the same project will be using the demonstration of old and new syntax.

Here I will be creating an Action Filter called LoggingFilter and will be logging some info before and after the execution of an action.

The above code is straightforward. So now we add this to the service collection.

To use this action filter with an Action method we will use Microsoft.AspNetCore.Mvc.ServiceFilter attribute which is using the old syntax which takes System.Type as a parameter for the constructor and the definition of ServiceFilterAttribute.

Next, we will use the action filter with the Get action method of the WeatherForecastController generated from the template.

Now you run the application and execute the GetWeatherForecast endpoint from the swagger app, you will see the information log in the output window as

Cool, Everything works great but How about Microsoft would have made the ServiceFilterAttribute generic and avoid the type parameter in the constructor? Well, we will do it and will write better code than Microsoft. :) Just kidding. Actually, this is the enhancement done in C#11, thanks to Microsoft for that.

To redefine the ServiceFilterAttribute using the Generic Attribute feature, let’s see the ServiceFilterAttribute code which is available under Microsoft.AspNetCore.Mvc namespace:

As per the above Microsoft code here, ServiceFilter takes Type as a parameter in the constructor which we pass as typeof(LoggingFilter) in the action method. So now we will recreate the ServiceFilterAttribute by defining a generic attribute and the syntax would be as:

public class MyServiceFilterAttribute<T> : Attribute

and the code as:

Wow, no more type parameters and a very convenient way of making the attribute class generic.

Again if you run the application, the logs will be displayed in the output window as with the earlier code.

No difference in functionality but a huge difference in terms of better code.

Here is the full code used for this example:
https://github.com/binodmahto/FunProjects/tree/main/CSharp11WebAPIDemo

To know more about C#11 new features please read through:
https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11

Hope you enjoyed the content, follow me for more like this, and please don’t forget to LIKE it. Happy programming.

Wednesday, 22 August 2012

Custom Handle Error Attribute to handle exception in MVC 3 app


Frnds.
In this post I am going to explain, How can we handle error in MVC  application through common custom error attribute.
First create the custom Error Handler Attribute class. So here is our attribute class:
 public class HandleExceptionAttribute : FilterAttribute, IExceptionFilter
    {
        public void OnException(ExceptionContext filterContext)
        {
            filterContext.ExceptionHandled = true;

            string controllerName = filterContext.RouteData.Values["controller"].ToString();
            string actionName = filterContext.RouteData.Values["action"].ToString();
            string exceptionMessage = string.Empty;

            //You may not need this tracing code. Ignore it!
            Trace.TraceError("Controller = {0} : Action = {1} : Message = {2} : StackTrace = {3}",
                controllerName,
                actionName,
                filterContext.Exception.Message,
                filterContext.Exception.StackTrace);

            //Below is the switch case to handle error for all the controller class in our
            //MVC application at one place.
            switch (controllerName)
            {
                case "State":
                    switch (actionName)
                    {
                        case "Create":
                            actionName = "Index";
                            exceptionMessage = "Sorry, an error occured on creating new State, Please try again.";
                            break;
                        case "Edit":
                            actionName = "Index";
                            exceptionMessage = "Sorry, an error occured on editing new State, Please try again.";
                            break;
                        case "Delete":
                            actionName = "Index";
                            exceptionMessage = "Sorry, an error occured on deleting new State, Please try again.";
                            break;
                        default:
                            break;
                    }
                    break;
                case "Account":
                    switch (actionName)
                    {
                        case "Register":
                            controllerName = "Register";
                            actionName = "Index";
                            exceptionMessage = "Sorry, an error occured on registering new User, Please try again.";
                            break;
                        case "Login":
                            controllerName = "Login";
                            actionName = "Index";
                            exceptionMessage = "Sorry, an error occured on Login, Please try again.";
                            break;

                    }

                    break;
            }

            //Creating ViewData Dictionary which will hold the error info to show to the end user.
            ViewDataDictionary viewData = new ViewDataDictionary();
            viewData.Add(new KeyValuePair<string, object>("controller", controllerName));
            viewData.Add(new KeyValuePair<string, object>("action", actionName));
            viewData.Add(new KeyValuePair<string, object>("message", exceptionMessage));

            //Calling error page inside shared folder to show the proper error message.
            filterContext.Result = new ViewResult { ViewName = "Error", ViewData = viewData };
        }
    }
Above attribute will help to handle error. You can add your controller and action method in switch case
statement as per your need.

Now Next step we will see how to use this attribute.
        [HandleException]
        public ActionResult Edit(StateModel stateModel)
        {
            try
            {
                // TODO: Add update logic here
                throw new InvalidOperationException();
            }
            catch(Exception ex)
            {
                throw ex;
            }
        }
Since my Custom Error Handler Attribute  is written to handle error for each action of controller so I need to use it before my action method. In above code [HandleException] attribute  will execute our Custom Handle Attribute class code written here in case of any error.
Next step we need to modify/create the error  page (Error.cshtml) inside shared folder to show the error message to the end user.
@{
    ViewBag.Title = "Error";
}

<h2>
    Sorry, @ViewData["message"].<br />
    <a href="@Url.Action(ViewData["action"].ToString(),ViewData["controller"].ToString())">Back</a>
</h2>
Note: Modify the above code as per your need and enjoy.
Thank You for reading.