Thursday, 29 September 2016

ASP.NET Core 1.0 MVC Controller

Controller is heart of MVC. It is a glue between View and Model. It accepts request as input through Action Methods, while an action method may generate some response (for example list page and details page) or perform some task (for example add, update and delete). In simple, we can call Controller a group of related Action Methods with set of applicable rules. And it is responsible to manipulate data in form of Models and rendering of views as final result.



It is not required, but we have few conventions, which are followed throughout community but in actual they are not required. They are just followed as best practice.
  • A Controller has Controller Suffix. This convention is only followed to avoid name conflict and to make it easier to identify Controllers.

  • A Controller has to be in Controller Folder. We know that final output assembly have no concern of folder structure used in code. So this convention is solely in practice for code maintenance.
  • A Controller must inherent from Microsoft.AspNetCore.Mvc.Controller Class directly or indirectly. This practice is mainly used as Microsoft.AspNetCore.Mvc.Controller class provides us many methods also called Controller Helper methods and properties out of box. Controller helper methods are generally used to process and return response:
    • View
      • View helper method allows controller to specify view and model to be used.
      •  return View();  
         return View("ViewName");  
         return View(someObject);  
         return View("ViewName", someObject);  
        
    • Http Status Code
      • Http Status Code helper methods are a set of methods, which are used to return Http status code of specific type. Few important examples are as following:
        • BadRequest
        • NoContent
        • NotFound
        • Forbid
        • Unauthorized
      •  return BadRequest();  
         return Unauthorized();  
    • Formatted Response
      • Formatted Response helper methods allows the action method to return result in specific format. Few important examples are as following:
        • Json
        • Content
        • File
      •  return Json(someObject);  
         return Content(someObject);  
    • Content negotiated response
      • Content Negotiated Response helper methods allow to return a content negotiated response instead of custom object. Few important examples are as following:
        • Created
        • CreatedAtRoute 
        • CreatedAtAction
        • ChallengeResult
      •  return Created(uri, someObject);  
    • Redirect
      • Redirect helper methods allow to redirect the flow of request to some other action methods or route. Few important examples are as following:
        • Redirect
        • LocalRedirect
        • RedirectToAction
        • RedirectToRoute
      •  return RedirectToAction("ActionName");  
         return RedirectToAction("ActionName", someObject);   
         return RedirectToAction("ControllerName", "ActionName", someObject);
    • Any Object or Type
      • If an action method just returns an object or a collection of objects then returned data is formatted based on request parameters. For ASP.NET Core Json is default option. 
        • Native Types (int, string)
        • Complex Type (any class or generics)
      •  return "Hello Controllers";  
         return someObject;  return ListOfObjects;

Action Method

Action Method is unit of Controller. It is any public method and may have parameter of native or complex type, for example, Index or List Action Method may not have any parameter, Get Action Method may have a parameter as Id and a Update or Create Action Method may have complex ViewModel as parameter. Action Method parameters are populated using Model Binding, please refer to ASP.NET Core 1.0 MVC Model for more details on Model Binding. It is not rough and tough rule, but in general, an action methods perform following set of tasks:
  • Process request parameters and map them to data provider components. We may also consider InputFormatter and Parameter Mapping in this area. It may also involve input Data Validation. For more details on Model Binding and Parameter Mapping, please refer to ASP.NET Core 1.0 MVC Model. We will discuss InputFormatter and Data Validation in detail in future sessions.
  • These data provider components can be any repository, any data service or any business logic component. For .NET Core it is recommended to use data provider components as services, and these services are generally made available to Controller using Dependency Injection. We will discuss Dependency Injection in detail in future sessions.
  • Process response data and map them to final response output. Depending upon type of application, action method selects the response type. In normal MVC application, we mostly cater with IActionResult. We may return view. Please refer to ASP.NET Core 1.0 MVC View for more details about Views.

Additional Features

There are many other features related with controllers like following, but we may discuss them in different sessions in future:
  • Filters
  • Exception Handling
  • Response Cache
  • Routing
  • Controller Scaffolding
  • Web API
  • Authentication and Authorization
  • Dependency Injection
  • Testing

Further Reading

For further details, please refer to official documentation at: https://docs.asp.net/en/latest/mvc/controllers/index.html.

Thursday, 22 September 2016

CRUD operations in ASP.NET Core 1.0 MVC Application Part 8

Let's discuss how to export data or list in CSV format. For this purpose, we will implement Custom  Response Formatter CsvOutputFormatter and a generic helper CsvWriter to convert any List of objects to CSV format. We are going to extend our application from last discussion  CRUD operations in ASP.NET Core 1.0 MVC Application Part 7.

Add CsvWriter in WebApplicationCore.NetCore

  • Open existing Solution in Visual Studio 2015.
  • Now add a new folder Common in WebApplicationCore.NetCore.
    • Open Context Menu of project >> Add >> New Folder.
    • Name it Common.
  • Now add a new class CsvWriter.cs in Common folder in WebApplicationCore.NetCore.
    • Open Add New Item Screen through Solution Context Menu of Common folder >> Add >> Class >> Installed >> .NET Core >> Class.
    • Name it CsvWriter.cs.
    • Click OK Button.
  • It will add a new class CsvWriter.cs in Common folder.
  • Add required implementation in CsvWriter to generate CSV string for provided list of objects. CsvWriter is a helper class and it provides facility to generate CSV of provided List of objects. We have changed it for our requirements, please refer to .NET Core CSV Writer for more details.

 public class CsvWriter
 {
    private const string DELIMITER = ",";

    public string Write(IList list, bool includeHeader = true)
    {
        StringBuilder sb = new StringBuilder();

        Type type = list.GetType().GetGenericArguments()[0];

        //Get property collection and set selected property list
        PropertyInfo[] properties = type.GetProperties();

        //Add Header Names to Csv 
        if (includeHeader)
        {
            sb.AppendLine(this.CreateCsvHeaderLine(properties));
        }

        //Iterate through data list collection
        foreach (var item in list)
        {
            sb.AppendLine(this.CreateCsvLine(item, properties));
        }

        return sb.ToString();
    }

    private string CreateCsvHeaderLine(PropertyInfo[] properties)
    {
        List<string> propertyValues = new List<string>();

        foreach (var prop in properties)
        {
            string formatString = string.Empty;
            string value = prop.Name;

            var attribute = prop.GetCustomAttribute(typeof(DisplayAttribute));
            if (attribute != null)
            {
                value = (attribute as DisplayAttribute).Name;
            }

            this.CreateCsvStringItem(propertyValues, value);
        }

        return this.CreateCsvLine(propertyValues);
    }

    private string CreateCsvLine(object item, PropertyInfo[] properties)
    {
        List<string> propertyValues = new List<string>();

        foreach (var prop in properties)
        {
            string formatString = string.Empty;
            object value = prop.GetValue(item, null);

            if (prop.PropertyType == typeof(string))
            {
                this.CreateCsvStringItem(propertyValues, value);
            }
            else if (prop.PropertyType == typeof(string[]))
            {
                this.CreateCsvStringArrayItem(propertyValues, value);
            }
            else if (prop.PropertyType == typeof(List<string>))
            {
                this.CreateCsvStringListItem(propertyValues, value);
            }
            else
            {
                this.CreateCsvItem(propertyValues, value);
            }
        }

        return this.CreateCsvLine(propertyValues);
    }

    private string CreateCsvLine(IList<string> list)
    {
        return string.Join(CsvWriter.DELIMITER, list);
    }

    private void CreateCsvItem(List<string> propertyValues, object value)
    {
        if (value != null)
        {
            propertyValues.Add(value.ToString());
        }
        else
        {
            propertyValues.Add(string.Empty);
        }
    }

    private void CreateCsvStringListItem(List<string> propertyValues, object value)
    {
        string formatString = "\"{0}\"";
        if (value != null)
        {
            value = this.CreateCsvLine((List<string>)value);
            propertyValues.Add(string.Format(formatString, this.ProcessStringEscapeSequence(value)));
        }
        else
        {
            propertyValues.Add(string.Empty);
        }
    }

    private void CreateCsvStringArrayItem(List<string> propertyValues, object value)
    {
        string formatString = "\"{0}\"";
        if (value != null)
        {
            value = this.CreateCsvLine(((string[])value).ToList());
            propertyValues.Add(string.Format(formatString, this.ProcessStringEscapeSequence(value)));
        }
        else
        {
            propertyValues.Add(string.Empty);
        }
    }

    private void CreateCsvStringItem(List<string> propertyValues, object value)
    {
        string formatString = "\"{0}\"";
        if (value != null)
        {
            propertyValues.Add(string.Format(formatString, this.ProcessStringEscapeSequence(value)));
        }
        else
        {
            propertyValues.Add(string.Empty);
        }
    }

    private string ProcessStringEscapeSequence(object value)
    {
        return value.ToString().Replace("\"", "\"\"");
    }
 }

Add CsvOutputFormatter in WebApplicationCore.NetCore

  • Now add a new class CsvOutputFormatter.cs in Common folder in WebApplicationCore.NetCore.
    • Open Add New Item Screen through Solution Context Menu of Common folder >> Add >> Class >> Installed >> .NET Core >> Class.
    • Name it CsvOutputFormatter.cs.
    • Click OK Button.
  • It will add a new class CsvOutputFormatter.cs in Common folder.
  • Implement Custom Output Formatter in CsvOutputFormatter class to generate response in CSV format using CsvWriter. To create a new Custom Output Formatter we can use one of following approaches:
    • Inherent existing output formatter base class TextOutputFormatter, OutputFormatter or StreamOutputFormatter as per requirement. For example, JsonOutputFormatter and XmlSerializerOutputFormatter inherit TextOutputFormatter. Although, this approach provides many features out of box due to base class, yet it makes formatter little heavier. 
    • Alternatively, Implement IOutputFormatter interface and we just have to implement two methods: CanWriteResult and WriteAsync. Where CanWriteResult is used to check if this formatter can be used to process response or not, while WriteAsync method performs actual processing on response. We have used this approach as it is simple and efficient.
    • ASP.NET Core provisions us to implement Custom Response Formatter and Custom Request Formatter separately. In this way, we can use any kind of Response and Request formatter as per requirements. We will discuss Custom Response and Request Formatter in detail in future sessions.
  • We also need to register CsvOutputFormatter, for this purpose, we register it in ConfigureServices method of Startup class with MVC service as Setup Action. In simple, we have to add CsvOutputFormatter in collection of OutputFormatters of MVC Service.

 public class CsvOutputFormatter : IOutputFormatter
 {
    public bool CanWriteResult(OutputFormatterCanWriteContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (context.ContentType == null || context.ContentType.ToString() == "text/csv")
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    public async Task WriteAsync(OutputFormatterWriteContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        var response = context.HttpContext.Response;
        response.ContentType = "text/csv";

        using (var writer = context.WriterFactory(response.Body, Encoding.UTF8))
        {
            IList lst = context.Object as IList;
            CsvWriter csvWriter = new CsvWriter();

            string csv = csvWriter.Write(lst, true);

            writer.Write(csv);

            await writer.FlushAsync();
        }
    }
 }


 public void ConfigureServices(IServiceCollection services)
 {
    // Add framework services.
    services.AddMvc(options=>options.OutputFormatters.Add(new CsvOutputFormatter()));

    services.AddSingleton<IConfigurationRoot>(sp => { return this.Configuration; });
    services.AddScoped<IContactDataAccess, ContactDataAccess>();
    services.AddScoped<IContactBusinessLogic, ContactBusinessLogic>();
 }

GetCsv Action Method and Get CSV Link

  • Now add a new action method GetCsv in WebApplicationCore.NetCore.ContactController class. It is similar to Index Action Method with exception that it returns List<ContactListVM> and has [Produces("text/csv")] attribute to mark content type to enforce ustilization of CsvOutputFormatter. We are explicitly checking content type == "text/csv" in CanWriteResult.
  •  Add Get CSV link in Contract\Index.cshtml for GetCsv action method.

 [Produces("text/csv")]
 public List<ContactListVM> GetCsv()
 {
    List<Contact> contacts = this.ContactBusinessLogic.GetContacts();
    List<ContactListVM> contactVMs = new List<ContactListVM>();
    ContactListVM contactVM;

    foreach (Contact contact in contacts)
    {
        contactVM = new ContactListVM
        {
            ContactId = contact.ContactId,
            ContactNumber = contact.ContactNumber,
            Email = contact.Email,
            Name = contact.Name,
            WebSite = contact.WebSite
        };
        contactVMs.Add(contactVM);
    }

    return contactVMs;
 }

 <th>
    <a asp-controller="Contact" asp-action="GetCsv">Get CSV</a>
 </th>

Run Application in Debug Mode

  • Press F5 or Debug Menu >> Start Debugging or Start IIS Express Button on Toolbar to start application in debugging mode.
  • It will show Home Page in browser.
  • Click Contact List Menu Open to open Contact List Page.
  • Click Get CSV link it will show open save dialog.
  • Save response as CSV.
  • Open file, it will open CSV file in default program, most probably in Excel or Notepad.

Sample Source Code

We have placed sample code for this session in "CRUD operations in ASP.NET Core 1.0 MVC Application Part 8_Code.zip" in https://aspdotnetcore.codeplex.com/SourceControl/latest CodePlex repository.


CRUD Operations in AP.NET Core 1.0 All Parts

CRUD operations in ASP.NET Core 1.0 MVC Application Part 1
CRUD operations in ASP.NET Core 1.0 MVC Application Part 2
CRUD operations in ASP.NET Core 1.0 MVC Application Part 3
CRUD operations in ASP.NET Core 1.0 MVC Application Part 4
CRUD operations in ASP.NET Core 1.0 MVC Application Part 5
CRUD operations in ASP.NET Core 1.0 MVC Application Part 6
CRUD operations in ASP.NET Core 1.0 MVC Application Part 7 
CRUD operations in ASP.NET Core 1.0 MVC Application Part 8

Monday, 19 September 2016

ASP.NET Core 1.0 MVC View

View is said to be UI part of a web application. In simple, it can be called HTML Template which is used to generate final HTML. It is important to note that Razor code is executed server side to generate final HTML of a page with Help of Razor Engine. ASP.NET Core MVC Views has .cshtml extension (for C# ) and by default these files are stored in Views folder. Generally, controller has its own folder with views for related controller action methods.



By use, there following main types of views:
  • Action Specific Views
  • Partial Views
  • Layouts
  • Special View

Action Specific Views

Action Specific View are called from some action method and they are generally stored in view folder related controller and by default have same name as of action method. We can call views of different name by specifying view name and from different folder by specifying full path from an action method. Generally full qualified path is like “~/Views/FolderPath/ViewName.cshtml” or with relative path “../FolderPath/ViewName.cshtml”. Please refer to ASP.NET Core 1.0 MVC Controller for more details about Controllers.

Partial views

Partial View are said to be reusable components and very similar to web control in ASP.Net Web Forms. They can also be used to decompose complex structures into smaller parts. But main purpose of them is reusability. Partial View is rendered within another view. Partial views has same extension like view .cshtml and it is not required but a practice to start partical view with underscore (_). Technically, partial view is same as normal view but we make its Layout null. Furthermore, special views like _ViewStart are not executed for partial views.
We can add partial view into a view with @Html.Partial(“_ViewName”), while partial view can have relative or full qualified path.  And if we have strongly typed partial view, then we may add partial view with @Html.Partial(“_ViewName”, dataModel).It is important to note that partial views can be nested. So we can add a partial view in another partial view. Within each view or partial view, relative paths are always relative to that view, not the root or parent view. We are not allowed to have partial views loop cycle (circular nesting).

Layouts

Layout Views are said to be main structure and they are very similar to Master Page in ASP.NET Web Forms. Layout View defines main structure of a web application and may contain header, menu, footer and page content area as per requirements. They facilitate us to define and handle all these at one place. It is common to have at least one layout view, but we can have more than one layout views to meet different requirements. For example, we can have different layouts for front office and back office. Or to have different layout for popups. It is customary to have layout view as “_Layout.cshtml” in Shared Folder of Views. We set layout of a page with following statement in view or in _ViewStart.cshtml.

 @{  
  Layout = “_Layout”;  
 }  

Layout view contain special tag @RenderBody() in which main called view is added and @RenderSection() which specifies section to be rendered in view. We will discuss sections in detail in future sessions.

Special View

Like Layout view, ASP.NET has couple of other special views:
  • _ViewImports.cshtml
  • _ViewStart.cshtml

_ViewImports.cshtml

_ViewImports view is used to perform activities like:  importing namespaces or performing dependency injection, shared directive. Generally, we have _ViewImports  at the root Views folder, but we can have as many as required. For example, we can add _ViewImports view in each folder. In this case current folder settings are added to settings from upper folder. And in case of conflict settings from from nearest _ ViewImports view get preference. The _ViewImports file supports the following directives:
  • @addTagHelper
  • @removeTagHelper
  • @tagHelperPrefix
  • @using
  • @model
  • @inherits
  • @inject

_ViewStart.cshtml

_ViewStart.cshtml is used to execute common tasks like setting Layout View. The statements listed in _ViewStart.cshtml are run before every view except layouts, and partial views. Generally, _ViewStart.cshtml is located in the Views folder. Like _ViewImports.cshtml every folder can have _ViewStart.cshtml. These are executed from parent to child in sequence.

View Discovery

View Discovery in the process to identify the viewed called by an action method or a partial view during rendering of final Html. If a view name is not specified or name without path is specified then this process determines which view file will be used based on predefined steps. For example, when an action returns the View without view name then action name is used as the view name. Similarly, runtime looks for a controller-specific view first, then looks for matching view name in the Shared folder. And when view is specified with a full qualified path then only specific view file is used.

View Categorization based on Model

We can categorize views on bases of Model or data manipulation as following:
  • Loosely Typed or Type Less Views
  • Strongly Typed Views
  • Dynamic Views

Loosely Typed or Type Less Views

We can pass data to views using loosely typed data collections: ViewData and ViewBag.

ViewData

ViewData is a dictionary object accessed through string keys. We can store and retrieve objects in it, and we may need to cast them to a specific type. We can use ViewData to pass data from a controller to views, as well as within views (and partial views and layouts). String data can be stored and used directly, without the need for a cast.
 In Action Method   
 public IActionResult About()  
 {  
   ViewData["Message"] = "Your application description page.";  
   return View();  
 }  
 In View  
 @{  
   ViewData["Title"] = "About";  
 }  
 <h2>@ViewData["Title"].</h2>  
 <h3>@ViewData["Message"]</h3>  

ViewBag

The ViewBag property is a wrapper around ViewData that provides a dynamic view over that collection. It is not a separate collection. The ViewBag objects provides dynamic access to the objects stored in ViewData. This can be more convenient to work with, since it doesn’t require casting.
 In Action Method   
 public IActionResult Contact()  
 {  
   ViewBag.Message = "Your contact page.";  
   return View();  
 }  
 In View  
 @{  
   ViewBag.Title = "Contact";  
 }  
 <h2>@ViewBag.Title</h2>  
 <h3>@ViewBag.Message</h3>  

Which One to Use

Although ViewBag is just a wraper on ViewData collection and we can mix and match between ViewData and ViewBag without any issue, yet it is better to use one approach in a project.

Strongly Typed Views

Strongly Typed View has a specific a model type in the view, and it is mapped to an instance of this type passed to the view from the action as parameter. We can specify a model for a view using the @model directive then the instance sent to the view can be accessed in a strongly-typed manner using @Model as object of specified type.It is highly practiced and is most recommended option to pass data as it gives lot of benefits like robustness, compilation protection over other approaches.
Although we can use any type as model, but it is recommend to use Plain Old CLR Object (POCO) ViewModels. Please refer to ASP.NET Core 1.0 MVC Model for more details about Model and ViewModel,

Dynamic Views

Dynamic Views are hybrid of both Strongly Typed and Loosely Typed Views. In this kind of view model is not declared but have a model instance passed to them. But @model and it's properties  can be used in view dynamically. In this case we don't have compilation protection or IntelliSense. If the property doesn’t exist, then application crashes at runtime.

Additional Features

There are many other features related with Views like following, but we may discuss them in different sessions in future:
  • Tag Helper
  • HTML Helper
  • View Scaffolding
  • Razor Engine

Further Reading

For further details, please refer to official documentation at: https://docs.asp.net/en/latest/mvc/views/index.html.

Sunday, 18 September 2016

ASP.NET Core 1.0 MVC Model

Model is an object representing data item and set of applicable operations. It can be of simple native type like: integer, string or complex structure like class, array. Ideally, model represents state of an application. It is said to be data holder in MVC structure.

For data centric applications, Model is representation of an Entity and is generally used to transport data between Database and View through controller. For example, in Entity Framework or any ORM, Model Classes represent underlying entities.



It is common practice to pass custom models to views called as ViewModel particularly in case of Strongly Typed Views. Please refer to ASP.NET Core 1.0 MVC View for more details about Views. There are many reasons and advantages to use View Models:
  • ViewModels can be subset of a single model or multiple models and they are designed to fulfill data requirements of view without effecting actual model. For example, for a list page, we may use a ViewModel which has as few fields as few column as required on list page instead of having all of fields. On the other hand, for a complex view we can have a composite ViewModel having fields from different Models or additional fields.
  • When we bind Model with View then they can make model details available in rendered Html, but with usage of ViewModel we can hide actual model details. Therefor they provide additional security.
  • It increase performance by minimizing data transfer to and from view. 
There are many other features related with Models like following:
  • Model Binding 
  • Data Annotation and Model Validation
  • Response Data Formatting

Model Binding

Model Binding or Data mapping is responsible to map data from request to actual action method. Please refer to ASP.NET Core 1.0 MVC Controller for more details about Controllers and Action Methods. For simplicity, we have to keep following points in mid:
  • Model Binding is based on key value pair mapping and this mapping is not case sensitive, Data Elements are mapped using reflection and recursion.
  • For data mapping following collections are checked order wise: Form, Route and then Query String. So for parameter data mapping first of all Form values are parsed, if parameter is not found in Form then Route Values are check, Even if value not found then Query String is check. If a value not found in any of these then it is tried to set it to null.
  • ASP.NET Core provide us flexibility to change default binding behavior through predefined attributes including following:
    • BindRequired
    • BindNever
    • FromHeader
    • FromQuery
    • FromRoute
    • FromForm
    • FromServices
    • FromBody
    • ModelBinder (It allows us to define custom data binder) 

Data Annotation and Model Validation

Data Annotation is used to decorate a model. These are Attribute and used  to add additional details or metadata to model. This additional set of information can be used for a variety of purposes like: Labeling, Data Validation, Data Formatting, and Data Mapping.
System.ComponentModel.DataAnnotations provides a set of validation attributes. Most of the time data annotations are used for Labeling and Data Validation. Most commonly used data annotations are:
  • Required
  • StringLength
  • MinLength
  • MaxLength
  • RegularExpression
  • DataType
  • Display
  • DisplayFormat
  • Compare
  • Editable
  • CustomValidation
Basically all validation data annotations are driven from ValidationAttribute, which is driven from Attribute. Please refer to CRUD operations in ASP.NET Core 1.0 MVC Application Part 4 to see most of data annotations in action.
We can provide custom message as per requirements through ErrorMessage and if we don't provide ErrorMessage then default message is used. We can also use localization through resource files to centrally control labels and messages in single or in multiple languages. For this purpose, we specify resource through ErrorMessageResourceType parameter and resource filed with ErrorMessageResourceName parameter. We can also define new data annotation to perform both of client side and server side validations. We will discuss creating new data annotations in detail in future sessions.

Required

RequiredAttribute inherits from ValidationAttribute and specifies that a data field value is required.
[Required]

StringLength

StringLengthAttribute inherits from ValidationAttribute and specifies the minimum and maximum length of characters that are allowed in a data field.
[StringLength(100, MinimumLength = 3)]

MinLength

MinLengthAttribute inherits from ValidationAttribute  and specifies the minimum length of array or string data.
[MinLength(3)] 

MaxLength

MaxLengthAttribute inherits from ValidationAttribute and specifies the maximum length of array or string data.
[MaxLength(100)]

RegularExpression

RegularExpressionAttribute inherits from ValidationAttribute and specifies that a data field value must match the specified regular expression
[RegularExpression("[a-zA-Z ]*$", ErrorMessage = "Name can only contain alphbetics and space.")]

DataType

DataTypeAttribute inherits from ValidationAttribute and specifies the name of an additional type to associate with a data field. We also have specific purpose data validators like CreditCard, EmailAddress, FileExtensions, Phone, Url driven from DataTypeAttribute. They are used to validate string values for specific format acceptable for credit card number, email address, file extensions, phone number and Urls.
[DataType(DataType.Time)]

Display

DisplayAttribute inherits from Attribute and provides a general-purpose attribute that lets you specify localizable strings. This is basically used to display label instead of validation.
[Display(Name = "Contact Id")]

DisplayFormat

DisplayFormatAttribute inherits from Attribute and specifies how data fields are displayed and formatted.
[DisplayFormat(DataFormatString ="9,999.##", ApplyFormatInEditMode =true)]

Compare

CompareAttribute inherits from ValidationAttribute and specifies that value of a data filed is compared tothe value of other data field.
[Compare("PropertyToCompare")]

Editable

EditableAttribute inherits from Attribute and specifies that data field is editable or not. 
[Editable(false)]

CustomValidation

CustomValidationAttribute inherits from ValidationAttribute and allows to specify a custom validation method that is used to validate a property or class instance. Where we can define our custom method to perform complex task like validating unique email, address validation or any complex validation activity.

Response Data Formatting

ASP.NET Core supports JSON as default data format for request, and we can also use XML by using Microsoft.AspNetCore.Mvc.Formatters.Xml package. Furthermore, we can have many other formats like CSV or any other as per requirements with custom implementations. We will discuss Response Data Formatting in detail in future sessions.

Further Reading

For further details, please refer to official documentation at:  https://docs.asp.net/en/latest/mvc/models/index.html.

Sunday, 11 September 2016

CRUD operations in ASP.NET Core 1.0 MVC Application Part 6

Let’s implement Delete Contact for our Contacts Application. We are going to extend our application from last discussion CRUD operations in ASP.NET Core 1.0 MVC Application Part 5.

Add DeleteContact Method in ContactBusinessLogic

  • Open existing Solution in Visual Studio 2015.
  • Open WebApplicationCore.NetCore.BusinessLogic.ContactBusinessLogic class.
  • Add new DeleteContact method.
  • It may delete existing contact from mockup list data.
    public bool DeleteContact(int contactId)
    {
        bool deleted = false;

        Contact contact = ContactBusinessLogic.Contacts.FirstOrDefault(c => c.ContactId == contactId);

        if (contact != null)
        {
            ContactBusinessLogic.Contacts.Remove(contact);
            deleted = true;
        }

        return deleted;
    }


Add DeleteContact Action Methods in ContactController

  • Add two new DeleteContact Methods.
    • One DeleteContact method with ContactId parameter and HttpGet attribute to explicitly specify that this is get method. It will take user to confirmation page.
    • Other DeleteContact method with parameter of ContactVM  type and HttpPost attribute to explicitly specify that this is post method. It will be called if user confirms to delete contact, after deleting contact list page will be loaded.
  • It looks odd to have two methods to delete a simple item. We can perform this task with single delete method and even without involving a view. Alternatively, we could have some confirmation dialog to perform delete. Or some other solution with single delete method. I also had such questions in my mind when I read about this practice for first time. Let me summarize this for you:
    • It may create a security holes as anyone can use use a direct link to attack and to delete data. Even a search engine crawler can hit this method unnecessarily. Although we can use Authorize filter to control Delete method call, yet I am convinced that it is an improvisation not a best solution.
    • And as per REST standards, Get method may not change data. And therefor it is not a good practice.
    • In simple, it is not a good practice to perform a delete operation with a get call.
    [HttpGet]
    public IActionResult DeleteContact(int id)
    {
        ContactVM contactVM = new ContactVM
        {
            ContactId = id
        };

        return View(contactVM);
    }

    [HttpPost]
    public IActionResult DeleteContact(ContactVM contactVM)
    {
        ContactBusinessLogic contactBL = new ContactBusinessLogic();

        contactBL.DeleteContact(contactVM.ContactId);

        return RedirectToAction("Index");
    }

Add Contact DeleteContact View

  • Add new View to Contact\Contact folder.
  • Open Add New Item Screen through Solution Context Menu of Contact >> Add >> New Item >> Installed >> .NET Core >> MVC View Page.
  • Name it DeleteContact.cshtml.
  • Click OK Button.
  • It will add a new view in Contact view folder.
    • Now, we have ContactVM objects as model.
    • Delete button to delete contact and Cancel button to return back to list page without deleting contact.
  • Change Index view implementation to add Delete Details option with each record.
@model ContactVM

<h2>Delete Contact</h2>

<form asp-action="DeleteContact">
    <input type="hidden" asp-for="ContactId" />
    <div class="form-horizontal">
        <div class="form-group">
            <label>Please confirm to delete contact</label>
            <div class="col-md-10">
                <input type="submit" value="Delete" class="btn btn-default" />
                <a asp-action="Index"class="btn btn-default">Cancel</a>
            </div>
        </div>
    </div>
</form>

<td>
    <a asp-controller="Contact" asp-action="GetContact" asp-route-id="@item.ContactId">Get Details</a> |
    <a asp-controller="Contact" asp-action="UpdateContact" asp-route-id="@item.ContactId">Edit Details</a> |
    <a asp-controller="Contact" asp-action="DeleteContact" asp-route-id="@item.ContactId">Delete Details</a>
</td>


Run Application in Debug Mode

  • Press F5 or Debug Menu >> Start Debugging or Start IIS Express Button on Toolbar to start application in debugging mode.
  • It will show Home Page in browser.
  • Click Contact List Menu Open to open Contact List Page.
  • Click Delete Details link to open Delete Contact Page and URL will change to http://localhost:21840/Contact/DeleteContact/3.
  • Click Delete button. It will delete contact and it will load contact list page.


Saturday, 10 September 2016

.NET Core 1.0 Connecting SQL Server Database

Let's discuss how to connect to databases. In this session, we will connect to SQL Server Database from .NET Core class library and we will use Microsoft SQL Server Database Provider named as "Microsoft.EntityFrameworkCore.SqlServer". Although, these are simple steps and can be performed in any project, but for simplicity and continuity of our work, we are going to use project created in our discussion Welcome to ASP.NET Core 1.0 MVC.

It is important to note that .NET Core does not have DataSet, DataTable and related objects anymore as of writing. But we have all of core features like Connection, Command, Paramter, DataReader and other related objects.

.NET Core Database Provider

A .NET Core application can connect to a database through Database Provider. Database Provider are database connectivity implementation for specific technology and are extension of System.Data.Common package.  At the moment .NET Core provides following Database Providers:
  • Microsoft SQL Server
  • SQLite
  • PostgreSQL
  • Microsoft SQL Server Compact Edition
  • IBM Data Servers
  • InMemory
  • MySQL (Under Devlopment)
  • Oracle (Under Devlopment)
Please refer to MSDN for more details on Database Providers.

Create Data Access Project

  • Open existing Solution in Visual Studio 2015.
  • Now add new Client Library .NET Core project in Solution.
    • Open Add New Project Screen through Solution Context Menu >> Add >> New Project Or File >> New >> Project.
    • Select Class Library (.NET Core) Template through Installed >> Templates >> Visual C# >> .NET Core.
    • Name project as “WebApplicationCore.NetCore.DataAccess”.
    • Set suitable location as “C:\ASP.NET Core\Welcome To .NET Core 1.0\ ASP.NET Core” (selected by default to solution root).
    • Click OK Button.
  • It will create a new class library project.
  • Add Reference to Microsoft.EntityFrameworkCore.SqlServer using one of following methods:
    • Open Package Manger Console through Tools >> NuGet Packet Manger >> Package Manger Console and run install command "Install-Package Microsoft.EntityFrameworkCore.SqlServer" for WebApplicationCore.NetCore.DataAccess project.
    • Open NuGet Manager through WebApplicationCore.NetCore.DataAccess Reference context menu >> References >> Manage NuGet  Packages. in Browse tab search for "Microsoft.EntityFrameworkCore.SqlServer" and install.
  • Rename Class1 as BaseDataAccess and add required implementation to connect to SQL Server Database. 
 public class BaseDataAccess
 {
    protected string ConnectionString { get; set; }

    public BaseDataAccess()
    {
    }

    public BaseDataAccess(string connectionString)
    {
        this.ConnectionString = connectionString;
    }

    private SqlConnection GetConnection()
    {
        SqlConnection connection = new SqlConnection(this.ConnectionString);
        if (connection.State != ConnectionState.Open)
            connection.Open();
        return connection;
    }

    protected DbCommand GetCommand(DbConnection connection, string commandText, CommandType commandType)
    {
        SqlCommand command = new SqlCommand(commandText, connection as SqlConnection);
        command.CommandType = commandType;
        return command;
    }

    protected SqlParameter GetParameter(string parameter, object value)
    {
        SqlParameter parameterObject = new SqlParameter(parameter, value != null ? value : DBNull.Value);
        parameterObject.Direction = ParameterDirection.Input;
        return parameterObject;
    }

    protected SqlParameter GetParameterOut(string parameter, SqlDbType type, object value = null, ParameterDirection parameterDirection = ParameterDirection.InputOutput)
    {
        SqlParameter parameterObject = new SqlParameter(parameter, type); ;

        if (type == SqlDbType.NVarChar || type == SqlDbType.VarChar || type == SqlDbType.NText || type == SqlDbType.Text)
        {
            parameterObject.Size = -1;
        }

        parameterObject.Direction = parameterDirection;

        if (value != null)
        {
            parameterObject.Value = value;
        }
        else
        {
            parameterObject.Value = DBNull.Value;
        }

        return parameterObject;
    }

    protected int ExecuteNonQuery(string procedureName, List<DbParameter> parameters, CommandType commandType = CommandType.StoredProcedure)
    {
        int returnValue = -1;

        try
        {
            using (SqlConnection connection = this.GetConnection())
            {
                DbCommand cmd = this.GetCommand(connection, procedureName, commandType);

                if (parameters != null && parameters.Count > 0)
                {
                    cmd.Parameters.AddRange(parameters.ToArray());
                }

                returnValue = cmd.ExecuteNonQuery();
            }
        }
        catch (Exception ex)
        {
            //LogException("Failed to ExecuteNonQuery for " + procedureName, ex, parameters);
            throw;
        }

        return returnValue;
    }

    protected object ExecuteScalar(string procedureName, List<SqlParameter> parameters)
    {
        object returnValue = null;

        try
        {
            using (DbConnection connection = this.GetConnection())
            {
                DbCommand cmd = this.GetCommand(connection, procedureName, CommandType.StoredProcedure);

                if (parameters != null && parameters.Count > 0)
                {
                    cmd.Parameters.AddRange(parameters.ToArray());
                }

                returnValue = cmd.ExecuteScalar();
            }
        }
        catch (Exception ex)
        {
            //LogException("Failed to ExecuteScalar for " + procedureName, ex, parameters);
            throw;
        }

        return returnValue;
    }

    protected DbDataReader GetDataReader(string procedureName, List<DbParameter> parameters, CommandType commandType = CommandType.StoredProcedure)
    {
        DbDataReader ds;

        try
        {
            DbConnection connection = this.GetConnection();
            {
                DbCommand cmd = this.GetCommand(connection, procedureName, commandType);
                if (parameters != null && parameters.Count > 0)
                {
                    cmd.Parameters.AddRange(parameters.ToArray());
                }

                ds = cmd.ExecuteReader(CommandBehavior.CloseConnection);
            }
        }
        catch (Exception ex)
        {
            //LogException("Failed to GetDataReader for " + procedureName, ex, parameters);
            throw;
        }

        return ds;
    }
 }

BaseDataAccess 

BaseDataAccess is a helper class which encapsulates all the implementation to connect and fetch data. It will not only help us to maintain database connectivity related code separately, but will also facilitate to easily replace SQL Database Provider with any other Data Provider as per requirements. We have explicitly returned bases classes DbConnection, DbCommand, DbParameter and DbDataReader instead of SqlConnection, SqlCommand, SqlParameter and SqlDataReader to abstract  database connectivity from implementer. In this way, we have to just change BaseDataAccess to target to some other database. We have following Components in this class:
  • ConnectionString
  • GetConnection
  • GetCommand
  • GetParameter
  • GetParameterOut
  • ExecuteNonQuery
  • ExecuteScalar
  • GetDataReader

ConnectionString

ConnectionString holds the connection string, we can either initialize directly from configurations by code or we can also initilize it throug parameterized constructor. We will initialize it with following value: "Server=SqlServerInstanceName;Database=DatabaseName;Trusted_Connection=True;MultipleActiveResultSets=true". 

GetConnection

GetConnection creates a new connection of SqlConnection type and return it after opening.

GetCommand

GetCommand creates a new command of SqlCommand according to specified parameters.

GetParameter

GetParameter creates a new parameter of SqlParameter and initialize it with provided value.

GetParameterOut

GetParameterOut creates a new parameter of SqlParameter type with parameter direct set to Output type.

ExecuteNonQuery

ExecuteNonQuery initializes connection, command and executes ExecuteNonQuery method of command object. Although the ExecuteNonQuery returns no rows, any output parameters or return values mapped to parameters are populated with data. For UPDATE, INSERT, and DELETE statements, the return value is the number of rows affected by the command. Please refer to MSDN for more details about SqlCommand.ExecuteNonQuery.

ExecuteScalar

ExecuteScalar initializes connection, command and executes  ExecuteScalar method of command object. Executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored.  Please refer to MSDN for more details about SqlCommand.ExecuteScalar.

ExecuteReader

ExecuteReader initializes connection, command and executes ExecuteReader method of command object. Provides a way of reading a forward-only stream of rows from a SQL Server database. We have explicitly omitted using block for connection as we need to return DataReader with open connection state. Now question raises that how will we handle connection close open, for this we have created DataReader with "CommandBehavior.CloseConnection", which means, connection will be closed as related DataReader is closed.  Please refer to MSDN for more details about SqlCommand.ExecuteReader and SqlDataReader.

Using BaseDataAccess 

We may recommend to use BaseDataAccess as base class of any other class, ideally your actual DataAccess component. If you think, you don't need full DataAccess layer, the you can make this concrete class by removing abstract keyword from declaration  and also make its protected methods to public/internal as per requirements. 

 public class TestDataAccess : BaseDataAccess
 {
    public TestDataAccess(string connectionString) : base(connectionString)
    {
    }

    public List<Test> GetTests()
    {
        List<Test> Tests = new List<Test>();
        Test TestItem = null;

        List<DbParameter> parameterList = new List<DbParameter>();
            
        using (DbDataReader dataReader = base.ExecuteReader("Test_GetAll", parameterList, CommandType.StoredProcedure))
        {
            if (dataReader != null && dataReader.HasRows)
            {
                while (dataReader.Read())
                {
                    TestItem = new Test();
                    TestItem.TestId = (int)dataReader["TestId"];
                    TestItem.Name = (string)dataReader["Name"];

                    Tests.Add(TestItem);
                }
            }
        }
        return Tests;
    }

    public Test CreateTest(Test Test)
    {
        List<DbParameter> parameterList = new List<DbParameter>();

        DbParameter TestIdParamter = base.GetParameterOut("TestId", SqlDbType.Int, Test.TestId);
        parameterList.Add(TestIdParamter);
        parameterList.Add(base.GetParameter("Name", Test.Name));

        base.ExecuteNonQuery("Test_Create", parameterList, CommandType.StoredProcedure);

        Test.TestId = (int)TestIdParamter.Value;

        return Test;
    }
 }

  public class Test  
  {  
    public object TestId { get; internal set; }  
    public object Name { get; internal set; }  
  }  

Connection String from Configurations

If we are interested to read Connection String from configurations then we may add reference to Microsoft.Extensions.Configuration.Abstractions and define a Construct with IConfiguration type of parameter to get connection string from configuratuions.



 public BaseDataAccess(IConfigurationRoot configuration)
 {
    this.ConnectionString = configuration["ConnectionStrings:DefaultConnection"];
 }

Thursday, 8 September 2016

CRUD operations in ASP.NET Core 1.0 MVC Application Part 5

Let’s add Update Contact page for our Contacts. We are going to extend our application from last discussion  CRUD operations in ASP.NET Core 1.0 MVC Application Part 4. And we will be using same existing ContactVM.

Add UpdateContact Method in ContactBusinessLogic

  • Open existing Solution in Visual Studio 2015.
  • Open WebApplicationCore.NetCore.BusinessLogic.ContactBusinessLogic class.
  • Add new UpdateContact method.
  • It may update existing contact in mockup list data.
     public bool UpdateContact(Contact contact)
    {
        bool updated = false;
        Contact existingContact = this.GetContact(contact.ContactId);

        if (existingContact != null)
        {
            existingContact.Address1 = contact.Address1;
            existingContact.Address2 = contact.Address2;
            existingContact.City = contact.City;
            existingContact.ContactNumber = contact.ContactNumber;
            existingContact.Country = contact.Country;
            existingContact.Email = contact.Email;
            existingContact.Name = contact.Name;
            existingContact.WebSite = contact.WebSite;
            existingContact.ZipPostalCode = contact.ZipPostalCode;
            updated = true;
        }

        return updated;
    }  


Add UpdateContact Action Methods in ContactController

  • Add two new UpdateContact Methods.
    • One CreateContact method with ContactId parameter and HttpGet attribute to explicitly specify that this is get method. It will be used to initialize and create Update Page. We will pass view a ContactVM having contact data.
      • Get Contact Data from Business Logic for given id.
      • Transform it into ContactVM and pass to view.
      • Logically, this is almost similar to GetContact. 
    • Other UpdateContact method with parameter of ContactVM  type and HttpPost attribute to explicitly specify that this is post method. It will be used to validate input at server side and to update object.
      • Post method may use ModelState.IsValid property to check if model does not have any error. 
      • Transform ContactVM object to Contact object and pass it to ContactBusinessLogic().UpdateContact for further processing.
      • If no error is found the and we have performed all activities successfully, then we can move to other page, for example, in our case we may navigate to main list page. Alternatively, we may return the same view with current object for reprocessing.
      • Logically, this is almost similar to CreateContact.
    • It is not required to have both methods same name, but it is conventional and in practice for a long time.
  • RedirectToAction is used to redirect to some other view.
    [HttpGet]
    public IActionResult UpdateContact(int id)
    {
        ContactBusinessLogic contactBL = new ContactBusinessLogic();
        Contact contact = contactBL.GetContact(id);

        ContactVM contactVM = new ContactVM
        {
            Address1 = contact.Address1,
            Address2 = contact.Address2,
            City = contact.City,
            ContactId = contact.ContactId,
            ContactNumber = contact.ContactNumber,
            Country = contact.Country,
            Email = contact.Email,
            Name = contact.Name,
            ProvinceState = contact.ProvinceState,
            WebSite = contact.WebSite,
            ZipPostalCode = contact.ZipPostalCode
        };

        return View(contactVM);
    }

    [HttpPost]
    public IActionResult UpdateContact(ContactVM contactVM)
    {
        if (this.ModelState.IsValid)
        {
            Contact contact = new Contact
            {
                Address1 = contactVM.Address1,
                Address2 = contactVM.Address2,
                City = contactVM.City,
                ContactId = contactVM.ContactId,
                ContactNumber = contactVM.ContactNumber,
                Country = contactVM.Country,
                Email = contactVM.Email,
                Name = contactVM.Name,
                ProvinceState = contactVM.ProvinceState,
                WebSite = contactVM.WebSite,
                ZipPostalCode = contactVM.ZipPostalCode
            };
            ContactBusinessLogic contactBL = new ContactBusinessLogic();                
            if (contactBL.UpdateContact(contact))
            {
                return RedirectToAction("Index");
            }
        }
        return View(contactVM);
    }

Add Contact UpdateContact View

  • Add new View to Contact\Contact folder.
  • Open Add New Item Screen through Solution Context Menu of Contact >> Add >> New Item >> Installed >> .NET Core >> MVC View Page.
  • Name it UpdateContact.cshtml.
  • Click OK Button.
  • It will add a new view in Contact view folder.
    • Now, we have ContactVM objects as model.
    • Form does not process disabled items, so we have added a hidden field for ContactId, to make it available in Post method, 
  • Change Index view implementation to add Update Contact option with each record.
@model ContactVM
<h2>Update Contact</h2>
<form asp-action="UpdateContact">
    <input type="hidden" asp-for="ContactId" />
    <div class="form-horizontal">
        <div asp-validation-summary="ModelOnly" class="text-danger"></div>
        <div class="form-group">
            <label asp-for="ContactId" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="ContactId" class="form-control" disabled="disabled" />
                <span asp-validation-for="ContactId" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="Name" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="Address1" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="Address1" class="form-control" />
                <span asp-validation-for="Address1" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="Address2" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="Address2" class="form-control" />
                <span asp-validation-for="Address2" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="City" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="City" class="form-control" />
                <span asp-validation-for="City" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="ProvinceState" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="ProvinceState" class="form-control" />
                <span asp-validation-for="ProvinceState" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="ZipPostalCode" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="ZipPostalCode" class="form-control" />
                <span asp-validation-for="ZipPostalCode" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="Country" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="Country" class="form-control" />
                <span asp-validation-for="Country" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="ContactNumber" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="ContactNumber" class="form-control" />
                <span asp-validation-for="ContactNumber" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="Email" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="Email" class="form-control" />
                <span asp-validation-for="Email" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="WebSite" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="WebSite" class="form-control" />
                <span asp-validation-for="WebSite" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Update" class="btn btn-default" />
            </div>
        </div>
    </div>
</form>
<p>
    <a asp-action="Index">Back to List</a>
</p>

 <td>
    <a asp-controller="Contact" asp-action="GetContact" asp-route-id="@item.ContactId">Get Details</a> | 
    <a asp-controller="Contact" asp-action="UpdateContact" asp-route-id="@item.ContactId">Edit Details</a>
 </td>


Run Application in Debug Mode

  • Press F5 or Debug Menu >> Start Debugging or Start IIS Express Button on Toolbar to start application in debugging mode.
  • It will show Home Page in browser.
  • Click Contact List Menu Open to open Contact List Page.
  • Click Edit Details link to open Update Contact Page and URL will change to http://localhost:21840/Contact/UpdateContact/3.
  • Edit contact data and click Update Button and it will update contact in the list.

CRUD operations in ASP.NET Core 1.0 MVC Application Part 7

Let's discuss how to connect to databases. We have already implemented basic CRUD operations with mockup data, now we are going to extend our application from last discussion  CRUD operations in ASP.NET Core 1.0 MVC Application Part 6 to connect with SQL Server Database. And we will be using DataAccess Project from .NET Core 1.0 Connecting SQL Server Database.
A .NET Core application can connect to a database through Database Provider. We are going to use a SQL Server Database ContactDB, therefor we are going to use Microsoft SQL Server Database Provider.

Create Data Access Project

  • Open existing Solution in Visual Studio 2015.
  • Add new WebApplicationCore.NetCore.DataAccess and add reference to Microsoft.EntityFrameworkCore.SqlServer and BaseDataAccess class following steps in .NET Core 1.0 Connecting SQL Server Database.
  • We are going to use IConfigurationRoot to get web application configurations so add Reference to Microsoft.Extensions.Configuration.Abstractions as following:
    • Open NuGet Manager through WebApplicationCore.NetCore.DataAccess Reference context menu >> References >> Manage NuGet  Packages. in Browse tab search for "Microsoft.Extensions.Configuration.Abstractions" and install.
  • Add new Constructor with parameter of IConfigurationRoot type to get configurations.
 public BaseDataAccess(IConfigurationRoot configuration)
 {
    this.ConnectionString = configuration["ConnectionStrings:DefaultConnection"];
 }



Add ContactDataAccess to WebApplicationCore.NetCore.DataAccess

  • Add reference to “WebApplicationCore.NetCore.Model” because we are going to use Contact Model Class in project.
    • Open WebApplicationCore.NetCore. BusinessLogic References >> Add References >> Reference Manager Screen >> Projects >> Solution >> Select WebApplicationCore.NetCore.Model.
    • Click OK Button.
  • Add new Interface.
    • Open Add New Item Screen through Solution Context Menu >> Add >> New Item >> Installed >> .NET Core >> Code >> Interface.
    • Name it IContactDataAccess.cs.
    • Click OK Button.
  • It will add a new interface in project. 
  • Define CRUD operations in interface.
  • Add new Class.
    • Open Add New Item Screen through Solution Context Menu >> Add >> Class >> Installed >> .NET Core >> Code >> Class.
    • Name it ContactDataAccess.cs.
    • Click OK Button.
  • It will add a new class in project. 
  • Make ContactDataAccess child of BaseDataAccess and implement IContactDataAccess.
  • Implement code to perform CRUD operation from database.
 public interface IContactDataAccess  
 {  
     Contact GetContact(int contactId);  
     List<Contact> GetContacts();  
     Contact CreateContact(Contact contact);  
     bool UpdateContact(Contact contact);  
     bool DeleteContact(int contactId);  
 }  

 public class ContactDataAccess : BaseDataAccess, IContactDataAccess
 {
    public ContactDataAccess(IConfigurationRoot configuration) : base(configuration)
    {
    }

    public Contact GetContact(int contactId)
    {
        Contact contactItem = null;

        List<DbParameter> parameterList = new List<DbParameter>();

        parameterList.Add(base.GetParameter("ContactId", contactId));

        using (DbDataReader dataReader = base.ExecuteReader("Contact_Get", parameterList, CommandType.StoredProcedure))
        {
            if (dataReader != null && dataReader.HasRows)
            {
                if (dataReader.Read())
                {
                    contactItem = new Contact();
                    contactItem.ContactId = (int)dataReader["ContactId"];
                    contactItem.Name = (string)dataReader["Name"];
                    contactItem.Address1 = (string)dataReader["Address1"];
                    contactItem.Address2 = (string)dataReader["Address2"];
                    contactItem.City = (string)dataReader["City"];
                    contactItem.ProvinceState = (string)dataReader["ProvinceState"];
                    contactItem.ZipPostalCode = (string)dataReader["ZipPostalCode"];
                    contactItem.Country = (string)dataReader["Country"];
                    contactItem.ContactNumber = (string)dataReader["ContactNumber"];
                    contactItem.Email = (string)dataReader["Email"];
                    contactItem.WebSite = (string)dataReader["WebSite"];
                }
            }
        }
        return contactItem;
    }

    public List<Contact> GetContacts()
    {
        List<Contact> contacts = new List<Contact>();
        Contact contactItem = null;

        List<DbParameter> parameterList = new List<DbParameter>();

        using (DbDataReader dataReader = base.ExecuteReader("Contact_GetAll", parameterList, CommandType.StoredProcedure))
        {
            if (dataReader != null)
            {
                while (dataReader.Read())
                {
                    contactItem = new Contact();
                    contactItem.ContactId = (int)dataReader["ContactId"];
                    contactItem.Name = (string)dataReader["Name"];
                    contactItem.Email = (string)dataReader["Email"];
                    contactItem.WebSite = (string)dataReader["WebSite"];

                    contacts.Add(contactItem);
                }
            }
        }
        return contacts;
    }

    public Contact CreateContact(Contact contact)
    {
        List<DbParameter> parameterList = new List<DbParameter>();

        DbParameter contactIdParamter = base.GetParameterOut("ContactId", SqlDbType.Int, contact.ContactId);
        parameterList.Add(contactIdParamter);
        parameterList.Add(base.GetParameter("Name", contact.Name));
        parameterList.Add(base.GetParameter("Address1", contact.Address1));
        parameterList.Add(base.GetParameter("Address2", contact.Address2));
        parameterList.Add(base.GetParameter("City", contact.City));
        parameterList.Add(base.GetParameter("ProvinceState", contact.ProvinceState));
        parameterList.Add(base.GetParameter("ZipPostalCode", contact.ZipPostalCode));
        parameterList.Add(base.GetParameter("Country", contact.Country));
        parameterList.Add(base.GetParameter("ContactNumber", contact.ContactNumber));
        parameterList.Add(base.GetParameter("Email", contact.Email));
        parameterList.Add(base.GetParameter("WebSite", contact.WebSite));

        base.ExecuteNonQuery("Contact_Create", parameterList, CommandType.StoredProcedure);

        contact.ContactId = (int)contactIdParamter.Value;

        return contact;
    }

    public bool UpdateContact(Contact contact)
    {
        bool updated = true;
        List<DbParameter> parameterList = new List<DbParameter>();

        parameterList.Add(base.GetParameter("ContactId", contact.ContactId));
        parameterList.Add(base.GetParameter("Name", contact.Name));
        parameterList.Add(base.GetParameter("Address1", contact.Address1));
        parameterList.Add(base.GetParameter("Address2", contact.Address2));
        parameterList.Add(base.GetParameter("City", contact.City));
        parameterList.Add(base.GetParameter("ProvinceState", contact.ProvinceState));
        parameterList.Add(base.GetParameter("ZipPostalCode", contact.ZipPostalCode));
        parameterList.Add(base.GetParameter("Country", contact.Country));
        parameterList.Add(base.GetParameter("ContactNumber", contact.ContactNumber));
        parameterList.Add(base.GetParameter("Email", contact.Email));
        parameterList.Add(base.GetParameter("WebSite", contact.WebSite));

        int returnValue = base.ExecuteNonQuery("Contact_Update", parameterList, CommandType.StoredProcedure);

        updated = returnValue > 0;

        return updated;
    }

    public bool DeleteContact(int contactId)
    {
        bool deleted = false;

        List<DbParameter> parameterList = new List<DbParameter>();

        parameterList.Add(base.GetParameter("ContactId", contactId));

        int returnValue = base.ExecuteNonQuery("Contact_Delete", parameterList, CommandType.StoredProcedure);

        deleted = returnValue > 0;

        return deleted;
    }
 }

Change WebApplicationCore.NetCore.BusinessLogic

  • Add reference to “WebApplicationCore.NetCore.DataAccess” in WebApplicationCore.NetCore.BusinessLogic because we are going to use ContactDataAccess in this project.
    • Open WebApplicationCore.NetCore. BusinessLogic References >> Add References >> Reference Manager Screen >> Projects >> Solution >> Select WebApplicationCore.NetCore.DataAccess.
    • Click OK Button.
  • Although we are not going to use Configuration in BusinessLogic, yet we may need it in future. To use IConfigurationRoot to get web application configurations, add Reference to Microsoft.Extensions.Configuration.Abstractions as following:
    • Open NuGet Manager through WebApplicationCore.NetCore.BusinessLogic Reference context menu >> References >> Manage NuGet  Packages. in Browse tab search for "Microsoft.Extensions.Configuration.Abstractions" and install.
  • Add new Interface.
    • Open Add New Item Screen through Solution Context Menu >> Add >> New Item >> Installed >> .NET Core >> Code >> Interface.
    • Name it IContactBusinessLogic.cs.
    • Click OK Button.
  • It will add a new interface in project. 
  • Define CRUD operations in interface.
  • Change ContactBusinessLogic to use ContactDataAccess instead of mockup data.

 public interface IContactBusinessLogic
 {
    Contact GetContact(int contactId);
    List<Contact> GetContacts();
    Contact CreateContact(Contact contact);
    bool UpdateContact(Contact contact);
    bool DeleteContact(int contactId);
 }

 public class ContactBusinessLogic: IContactBusinessLogic
 {
    private IConfigurationRoot Configuration;
    private IContactDataAccess ContactDataAccess;

    public ContactBusinessLogic(IContactDataAccess contactDataAccess, IConfigurationRoot configuration)
    {
        this.ContactDataAccess = contactDataAccess;
        this.Configuration = configuration;
    }

    public Contact GetContact(int contactId)
    {
        return this.ContactDataAccess.GetContact(contactId);
    }

    public List<Contact> GetContacts()
    {
        return this.ContactDataAccess.GetContacts();
    }

    public Contact CreateContact(Contact contact)
    {
        return this.ContactDataAccess.CreateContact(contact);
    }

    public bool UpdateContact(Contact contact)
    {
        return this.ContactDataAccess.UpdateContact(contact);
    }

    public bool DeleteContact(int contactId)
    {
        return this.ContactDataAccess.DeleteContact(contactId);
    }
 }

Changes in WebApplicationCore.NetCore.Startup 

  • Open Startup.cs in WebApplicationCore.NetCore project.
  • open WebApplicationCore.NetCore.Startup and add service dependencies in ConfigureServices method for Configuration,  ContactDataAccess and ContactBusinessLogic.
  • Update ContractController class to use ContactBusinessLogic object available from constructor through dependency injection. 
 public void ConfigureServices(IServiceCollection services)
 {
    // Add framework services.
    services.AddMvc();
                        
    services.AddSingleton<IConfigurationRoot>(sp => { return this.Configuration; });
    services.AddScoped<IContactDataAccess, ContactDataAccess>();
    services.AddScoped<IContactBusinessLogic, ContactBusinessLogic>();
 }




 public class ContactController : Controller
 {
    private IContactBusinessLogic ContactBusinessLogic;
    private IConfigurationRoot Configuration;
    private IContactDataAccess ContactDataAccess;

    public ContactController(IContactBusinessLogic contactBusinessLogic, IContactDataAccess contactDataAccess, IConfigurationRoot configuration)
    {
        this.ContactBusinessLogic = contactBusinessLogic;
        this.ContactDataAccess = contactDataAccess;
        this.Configuration = configuration;
    }

    // GET: /<controller>/
    public IActionResult Index()
    {
        List<Contact> contacts = this.ContactBusinessLogic.GetContacts();
        List<ContactListVM> contactVMs = new List<ContactListVM>();
        ContactListVM contactVM;

        foreach (Contact contact in contacts)
        {
            contactVM = new ContactListVM
            {
                ContactId = contact.ContactId,
                ContactNumber = contact.ContactNumber,
                Email = contact.Email,
                Name = contact.Name,
                WebSite = contact.WebSite
            };
            contactVMs.Add(contactVM);
        }

        return View(contactVMs);
    }

    public IActionResult GetContact(int id)
    {
        Contact contact = this.ContactBusinessLogic.GetContact(id);

        ContactVM contactVM = new ContactVM
        {
            Address1 = contact.Address1,
            Address2 = contact.Address2,
            City = contact.City,
            ContactId = contact.ContactId,
            ContactNumber = contact.ContactNumber,
            Country = contact.Country,
            Email = contact.Email,
            Name = contact.Name,
            ProvinceState = contact.ProvinceState,
            WebSite = contact.WebSite,
            ZipPostalCode = contact.ZipPostalCode
        };

        return View(contactVM);
    }

    [HttpGet]
    public IActionResult CreateContact()
    {
        ContactVM contactVM = new ContactVM();

        return View(contactVM);
    }

    [HttpPost]
    public IActionResult CreateContact(ContactVM contactVM)
    {
        if (this.ModelState.IsValid)
        {
            Contact contact = new Contact
            {
                Address1 = contactVM.Address1,
                Address2 = contactVM.Address2,
                City = contactVM.City,
                ContactId = contactVM.ContactId,
                ContactNumber = contactVM.ContactNumber,
                Country = contactVM.Country,
                Email = contactVM.Email,
                Name = contactVM.Name,
                ProvinceState = contactVM.ProvinceState,
                WebSite = contactVM.WebSite,
                ZipPostalCode = contactVM.ZipPostalCode
            };

            contact = this.ContactBusinessLogic.CreateContact(contact);

            if (contact.ContactId > 0)
            {
                return RedirectToAction("Index");
            }
        }

        return View(contactVM);
    }

    [HttpGet]
    public IActionResult UpdateContact(int id)
    {
        Contact contact = this.ContactBusinessLogic.GetContact(id);

        ContactVM contactVM = new ContactVM
        {
            Address1 = contact.Address1,
            Address2 = contact.Address2,
            City = contact.City,
            ContactId = contact.ContactId,
            ContactNumber = contact.ContactNumber,
            Country = contact.Country,
            Email = contact.Email,
            Name = contact.Name,
            ProvinceState = contact.ProvinceState,
            WebSite = contact.WebSite,
            ZipPostalCode = contact.ZipPostalCode
        };

        return View(contactVM);
    }

    [HttpPost]
    public IActionResult UpdateContact(ContactVM contactVM)
    {
        if (this.ModelState.IsValid)
        {
            Contact contact = new Contact
            {
                Address1 = contactVM.Address1,
                Address2 = contactVM.Address2,
                City = contactVM.City,
                ContactId = contactVM.ContactId,
                ContactNumber = contactVM.ContactNumber,
                Country = contactVM.Country,
                Email = contactVM.Email,
                Name = contactVM.Name,
                ProvinceState = contactVM.ProvinceState,
                WebSite = contactVM.WebSite,
                ZipPostalCode = contactVM.ZipPostalCode
            };

            if (this.ContactBusinessLogic.UpdateContact(contact))
            {
                return RedirectToAction("Index");
            }
        }

        return View(contactVM);
    }

    [HttpGet]
    public IActionResult DeleteContact(int id)
    {
        ContactVM contactVM = new ContactVM
        {
            ContactId = id
        };

        return View(contactVM);
    }

    [HttpPost]
    public IActionResult DeleteContact(ContactVM contactVM)
    {
        this.ContactBusinessLogic.DeleteContact(contactVM.ContactId);

        return RedirectToAction("Index");
    }
 }


Run Application in Debug Mode


  • Press F5 or Debug Menu >> Start Debugging or Start IIS Express Button on Toolbar to start application in debugging mode.
  • It will show Home Page in browser.
  • Click Contact List Menu Open to open Contact List Page, so we have performed Get All operation.
  • Now perform all of following operations:
    • Create New Contact.
    • Open Details of a Contact.
    • Edit and Update a Contact.
    • Delete a Contact.
    • We can observe that changes are persistent as now values have been stored in database.

Sample Contact Database

We are using a test database named ContactDB having single table Contact and Stored Procedures Contact_GetAll, Contact_Get, Contact_Create, Contact_Update and Contact_Delete for  GetAll, Get, Create, Update and Delete operations respectively. We have Scripts available to create this database in Solution as "ContractDB.sql", and to insert test data as "ContactTestData.sql". We are using SQL Server 2014 Express, you can download it from MSDN.  We are not going to discuss SQL Server Database in this article.


Sample Source Code

We have placed sample code for this session in "CRUD operations in ASP.NET Core 1.0 MVC Application Part 7_Code.zip" in https://aspdotnetcore.codeplex.com/SourceControl/latest CodePlex repository.


CRUD Operations in AP.NET Core 1.0 All Parts

CRUD operations in ASP.NET Core 1.0 MVC Application Part 1
CRUD operations in ASP.NET Core 1.0 MVC Application Part 2
CRUD operations in ASP.NET Core 1.0 MVC Application Part 3
CRUD operations in ASP.NET Core 1.0 MVC Application Part 4
CRUD operations in ASP.NET Core 1.0 MVC Application Part 5
CRUD operations in ASP.NET Core 1.0 MVC Application Part 6
CRUD operations in ASP.NET Core 1.0 MVC Application Part 7 
CRUD operations in ASP.NET Core 1.0 MVC Application Part 8