Skip to main content

Centralized model validation both for MVC/WebApi and SPA client-side validation using FluentValidation

Validation is one of the crucial parts of any application. It has to validate on both client side and server side requests.

What are target features or implementation from this article?

  • Model validation for any given model.
  • Centralized/One code for validation on both server-side and client-side.
  • Automatic validation of model without writing any extra codes on/under actions for validation.
  •  NO EXTRA/ANY codes on client-side to validate any form.
  • Compatible with SPA.
  • Can be compatible with any client-side validation framework/library. Like Angular Reactive form validation or any jquery validation libraries.

Tools used in the implementation?

  • FluentValidation: I feel DataAnnotation validation are excellent and simple to use, but in case of complex validation or writing any custom validations are always tricker and need to write a lot of codes to achieve whereas FluentValidations are simple even in case of complex validation. Generally, we need to validate incoming input against database values, which are straight-forward in FluentValidation. Also, it is not bound to be used only with MVC it can implement at any layers or any front-end, and on the entry point, it just need initialization. Also, the Fluent approach is similar to Entity Framework Fluent mapping so an identical pattern.
  • jquery-validation: In case of MVC and as long as cshtml/Views are used it is effortless to implement client-side validation since it is automatically taken care by auto-generated unobtrusive validation under rendered HTML. No work at all there unless some remote validations are required. In case of SPA, it gets hugely affected since rendering engines are on client-side and Microsoft Unobtrusive validation won't play a role.

    jquery-validation works based on JSON data, we would be getting rules and messages from server side and applying validation on the form through centralized code implementation. So, one code would take care of everything.
  • MVC and ActionFilterAttribute: The filter to generate validation rules based on JS validation library, validate model when posted on the server and remote validation for given field if it needs any check against database/server side codes.
Now, it is time to look into coding I have already pushed changes in GitHub repository https://github.com/viku85/CustomSpaApp. Also, this article could be treated as a continuation of SPA architecture that I had done in my last blog article https://vikutech.blogspot.in/2017/10/custom-spa-engine-similar-to-mvc-pattern-by-using-typescript.html.

Composing Fluent validation for the view model

Let's first directly begin with composing fluent validation and later look into Filter implementation. The validation is straightforward but a bit of enhancement through RuleSet and implementation of a little wrapper on base class BaseValidation.

 /// <summary>  
 /// Fluent validation for <see cref="ContactViewModel"/> view model.  
 /// </summary>  
 /// <seealso cref="BaseValidation{ContactViewModel}" />  
 public class ContactViewModelValidation  
   : BaseValidation<ContactViewModel>  
 {  
   /// <summary>  
   /// Initializes a new instance of the <see cref="ContactViewModelValidation"/> class.  
   /// </summary>  
   public ContactViewModelValidation()  
   {  
     RuleFor(model => model.Name)  
       .NotEmpty()  
       .Length(3, 100);  
     RuleFor(model => model.Email)  
       .NotEmpty()  
       .EmailAddress();  
     RuleFor(model => model.Message)  
       .NotEmpty()  
       .Length(5, 2000);  
     RuleFor(model => model.Subject)  
       .NotEmpty();  
     RuleSet(OperationRequest.Insert, () =>  
     {  
       RuleFor(model => model.Message)  
               .Must((model, field, token) =>  
             {  
               // TODO: Check against database if Email and message already exists.  
               return false;  
             }).WithMessage("Your information is already submitted.")  
             ;//.When(model => !String.IsNullOrEmpty(model.Message) && String.IsNullOrEmpty(model.Email));  
     });  
   }  
 }  

The above looks pretty simplified, and names say it all. The little twist is, it can hold validation rules for any addition, update or deletion of a model. An example can be seen from above through RuleSet of Insert through OperationRequest.Insert enum.

The above code includes Common rules for any CRUD operation which would be shared in all operations.

Here is code for OperationRequest enum and Base class from above.

Enum
   /// <summary>  
   /// Operation request  
   /// </summary>  
   public enum OperationRequest  
   {  
     /// <summary>  
     /// The insert request  
     /// </summary>  
     Insert,  
     /// <summary>  
     /// The update request  
     /// </summary>  
     Update,  
     /// <summary>  
     /// The delete request  
     /// </summary>  
     Delete  
   }  


Base Class
 /// <summary>  
 /// Base validation for all Fluent validations.  
 /// </summary>  
 /// <typeparam name="TModel">The type of the model.</typeparam>  
 /// <seealso cref="AbstractValidator{TModel}" />  
 public class BaseValidation<TModel>  
   : AbstractValidator<TModel>  
 {  
   /// <summary>  
   /// Defines a RuleSet that can be used to group together several validators.  
   /// </summary>  
   /// <param name="ruleSetName">The name of the ruleset.</param>  
   /// <param name="action">Action that encapsulates the rules in the ruleset.</param>  
   protected void RuleSet(OperationRequest ruleSetName, Action action)  
   {  
     RuleSet(ruleSetName.ToString(), action);  
   }  
 }  

Above two items are simple enough, after this, we just need to register it in MVC service collection for dependency injection.

We are not going to register in-built fluent validation on assembly level instead register on DI and use it on our custom smart filter for validation of actions and generation of JSON for client-side validation.

 /// <summary>  
 /// Extension to Register all Fluent validations.  
 /// </summary>  
 public static class ExtensionRegisterValidation  
 {  
   /// <summary>  
   /// Adds FluentValidation for the application.  
   /// </summary>  
   /// <param name="services">The services.</param>  
   /// <returns>The Service collection.</returns>  
   public static IServiceCollection AddFluentValidation(this IServiceCollection services)  
   {  
     services.AddScoped<IValidator<ContactViewModel>, ContactViewModelValidation>();  
     return services;  
   }  
 }  

Startup.cs
 services.AddFluentValidation();  

That completes FluentValidation part.

Filter to auto-trigger FluentValidation and generation of client-side validation rules along with support of remote validation

I have already explained three responsibilities of same, let's look in detail:
  • Model validation for action. We discussed four modes of  FluentValidation including Common for all kind of CRUD operation using OperationRequest seen earlier. If you see GetValidationRule function it retrieves RuleSet for FluentValidation. Common RuleSet is named as Default under FluentValidation. While creating Fluent Validator, we can specify an array of RuleSets. The function generates RuleSet based on Action name in MVC/WebApi so that we do not have to put Validate filter by specifying RuleSets manually. Do modify based on your need as of current implementation it pulls from action name containing add, create, insert, delete, update. So, action name like CreateCustomer, AddCustomer, UpdateCustomerAsync would be automatically resolved.
  • Remote validation: In this, an AJAX call would be made for a field to validate on the server side. The trick is, it would request the same URL with query string validate with property name to validate. From code level, you can see some condition with validate checks are done for the same reason. A JsonResult is sent from this. An example:
    Remote Validation
    AJAX/Remote validation for Message field.
  • Validation Rules and messages: This is a static data served when a form is rendered, this would contain necessary information about validations for fields used in the form. This, JSON generation is achieved through JqueryValidatorProvider class implementation, and the result is this:
Validation Rules for the entire form generated from JqueryValidatorProvider 

Codes of Action filter attribute

 /// <summary>  
 /// Validation filter for model state validation.  
 /// </summary>  
 /// <seealso cref="ActionFilterAttribute" />  
 public sealed partial class ValidationResponseFilterAttribute  
   : ActionFilterAttribute  
 {  
   /// <summary>  
   /// Action execution on any controller.  
   /// </summary>  
   /// <param name="context">Action context.</param>  
   public override void OnActionExecuting(ActionExecutingContext context)  
   {  
     if (context == null)  
     {  
       return;  
     }  
     var model = GetTheActionArgument(context);  
     if (model == null && context.HttpContext.Request.Query.TryGetValue("validate", out var _))  
     {  
       context.Result = new JsonResult(string.Empty);  
       return;  
     }  
     if (model == null)  
     {  
       return;  
     }  
     var validator = context.HttpContext.RequestServices  
       .GetService(typeof(IValidator<>)  
       .MakeGenericType(model.ModelType)) as IValidator;  
     // In case of no validator and URL requested for validation rule  
     if (validator == null && context.HttpContext.Request.Query.Keys.Any(val => val == "validation"))  
     {  
       context.Result = new EmptyResult();  
       return;  
     }  
     if (validator == null)  
     {  
       return;  
     }  
     if (context.HttpContext.Request.Query.TryGetValue("validation", out var clientRuleRequest))  
     {  
       switch (clientRuleRequest)  
       {  
         default:  
           context.Result = new JqueryValidatorProvider(validator).GetValidationRules();  
           break;  
       }  
       return;  
     }  
     model.ExecuteOnModel((modelValue) =>  
     {  
       var validationResult =  
         validator.Validate(new ValidationContext(  
           modelValue,  
           new PropertyChain(),  
           new RulesetValidatorSelector(GetValidationRule(context))));  
       validationResult.AddToModelState(context.ModelState, null);  
     });  
     // If requested for Ajax validation.  
     if (context.HttpContext.Request.Query.TryGetValue("validate", out var modelProperty))  
     {  
       context.Result = new JsonResult(context.ModelState.ToDictionary(  
               kvp => kvp.Key,  
               kvp => kvp.Value.Errors.Select(e => e.ErrorMessage).Humanize()));  
       return;  
     }  
     if (!context.ModelState.IsValid)  
     {  
       context.Result = new BadRequestObjectResult(context.ModelState);  
     }  
   }  
   /// <summary>  
   /// Gets the action argument.  
   /// </summary>  
   /// <param name="actionContext">The action context.</param>  
   /// <returns>Object type and object.</returns>  
   private static ModelDefination GetTheActionArgument(ActionExecutingContext actionContext)  
   {  
     return (from arg in actionContext.ActionArguments.Values  
         let typ = arg?.GetType()  
         where typ != null && !typ.IsPrimitive && !typ.IsValueType && typ != typeof(string)  
         //&& typ != typeof(Kendo.Mvc.UI.DataSourceRequest) TODO: Note could exclude any other types  
         select new ModelDefination(arg)).FirstOrDefault();  
   }  
   /// <summary>  
   /// Gets the validation rule set names for Fluent validation.  
   /// </summary>  
   /// <param name="actionContext">The action context.</param>  
   /// <returns>Validation rules for Fluent mapping</returns>  
   private static string[] GetValidationRule(ActionExecutingContext actionContext)  
   {  
     var ruleSetNames = new List<string>(2) { "default" };  
     if (actionContext.ActionDescriptor is ControllerActionDescriptor descriptor)  
     {  
       if (descriptor.ActionName.Contains(nameof(OperationRequest.Delete)))  
       {  
         ruleSetNames.Add(OperationRequest.Delete.ToString());  
       }  
       else if (descriptor.ActionName.Contains("Create") ||  
         descriptor.ActionName.Contains("Add") ||  
         descriptor.ActionName.Contains(nameof(OperationRequest.Insert)))  
       {  
         ruleSetNames.Add(OperationRequest.Insert.ToString());  
       }  
       else if (descriptor.ActionName.Contains(nameof(OperationRequest.Update)))  
       {  
         ruleSetNames.Add(OperationRequest.Update.ToString());  
       }  
     }  
     return ruleSetNames.ToArray();  
   }  
 }  

JqueryValidatorProvider

This class is responsible for generating JSON for general form validation. This is done by looking into FluentValidation models meta information and ultimately creating JSON which helps in form validation.
The implementation could be changed based on your choice of the client-side validator, and server-side codes would accordingly change.
 /// <summary>  
 /// jQuery.Validator rules provider.  
 /// </summary>  
 public class JqueryValidatorProvider  
 {  
   /// <summary>  
   /// The validator  
   /// </summary>  
   private readonly IValidator Validator;  
   /// <summary>  
   /// Initializes a new instance of the <see cref="JqueryValidatorProvider"/> class.  
   /// </summary>  
   /// <param name="validator">The validator.</param>  
   public JqueryValidatorProvider(IValidator validator)  
   {  
     Validator = validator;  
   }  
   /// <summary>  
   /// Gets the validation rules based on jQuery.validator.  
   /// </summary>  
   /// <returns>Result along with rules.</returns>  
   public IActionResult GetValidationRules()  
   {  
     if (Validator == null)  
     {  
       return new EmptyResult();  
     }  
     var validationObject = new Dictionary<string, object>();  
     var errorObject = new Dictionary<string, Dictionary<string, string>>();  
     var validatorDescriptor = Validator.CreateDescriptor();  
     foreach (var valida in validatorDescriptor.GetMembersWithValidators())  
     {  
       var validations = new Dictionary<string, object>();  
       var errors = new Dictionary<string, string>();  
       foreach (var propertyValidator in valida)  
       {  
         var validatorAdded = false;  
         Action<string, object, Action<MessageFormatter, Action<Func<string, string>>>> add = (validationType, value, format) =>  
         {  
           validatorAdded = true;  
           var formatter = new MessageFormatter();  
           formatter.AppendPropertyName(valida.Key.Humanize());  
           var template = propertyValidator.ErrorMessageSource.GetString(null);  
           format?.Invoke(formatter, (d) => { template = d?.Invoke(template); });  
           validations.SafeAdd(validationType, value);  
           errors.SafeAdd(validationType, formatter.BuildMessage(template));  
         };  
         if (propertyValidator is NotNullValidator  
           || propertyValidator is NotEmptyValidator)  
         {  
           add("required", true, null);  
         }  
         if (propertyValidator is EmailValidator)  
         {  
           add("email", true, null);  
         }  
         if (propertyValidator is LengthValidator lengthValidator)  
         {  
           if (lengthValidator.Max > 0)  
           {  
             add(  
               "maxlength",  
               lengthValidator.Max,  
             (formatter, template) =>  
             {  
               formatter.AppendArgument("MinLength", lengthValidator.Min);  
               formatter.AppendArgument("MaxLength", lengthValidator.Max);  
               template?.Invoke((tem) => !tem.Contains("TotalLength") ? tem :  
                   tem.Split(".")?.FirstOrDefault());  
             });  
           }  
           add(  
             "minlength",  
             lengthValidator.Min,  
             (formatter, template) =>  
             {  
               formatter.AppendArgument("MinLength", lengthValidator.Min);  
               formatter.AppendArgument("MaxLength", lengthValidator.Max);  
               template?.Invoke((tem) => !tem.Contains("TotalLength") ? tem :  
               tem.Split(".")?.FirstOrDefault());  
             });  
         }  
         if (propertyValidator is RegularExpressionValidator expressionValidator)  
         {  
           add("regex", expressionValidator.Expression, null);  
         }  
         if (!validatorAdded)  
         {  
           add("remote", valida.Key, null);  
         }  
       }  
       validations.IfNotEmpty(() => validationObject.Add(valida.Key, validations));  
       errors.IfNotEmpty(() => errorObject.Add(valida.Key, errors));  
     }  
     return new JsonResult(new  
     {  
       rules = validationObject,  
       messages = errorObject  
     });  
   }  
 }  

From codes, remote validation would only enable when it did not fall in any category of validation.
That's an end of server-side codes. Client-side codes are relatively simple.

Client-side codes for validation

The client-side validation is entirely dependent on jquery-validation plugin since it is our choice for client-side validation.

FormValidation.ts
 import { injectable } from "inversify";  
 import { HttpRequestResponse } from './../RequestResponse/HttpRequestResponse';  
 @injectable()  
 class FormValidation {  
   private AppendError(form: JQuery<HTMLFormElement> | JQuery<HTMLElement>,  
     error: JQuery<HTMLElement>, element: JQuery<HTMLElement>) {  
     let errorSelector = $(`#${$(element).attr("id")}Error`);  
     errorSelector = errorSelector.length ? errorSelector : $(`#${$(element).attr("id")}-error`);  
     if (errorSelector.length) {  
       errorSelector.removeClass('field-validation-valid').addClass('field-validation-error');  
       errorSelector.text(error.text());  
     }  
     else {  
       $(element)  
         .parent().after(  
         `<div id='${$(element).attr('name')}Holder' class="field-validation-error"></div>`);  
       $(`#${(element).attr('name')}Holder`).append(error);  
     }  
   }  
   ParseModelStateError(data: JQuery.jqXHR<any>, eachErrorCallback: (message: string, propName: string) => void) {  
     if (data == undefined || data.responseJSON == undefined) {  
       return;  
     }  
     var message = '';  
     var propStrings = Object.keys(data.responseJSON);  
     $.each(propStrings, (errIndex, propString) => {  
       var propErrors = data.responseJSON[propString];  
       $.each(propErrors, (errMsgIndex, propError) => {  
         message += propError;  
       });  
       message += '\n';  
       eachErrorCallback(message, propString);  
       message = '';  
     });  
   }  
   private RemoteValidation(elementName: string,  
     form: JQuery<HTMLFormElement> | JQuery<HTMLElement>) {  
     let getData = () => {  
       let serializedData = {};  
       $(form).serializeArray()  
         .map((key) => { serializedData[key.name] = key.value; });  
       return JSON.stringify(serializedData);  
     }  
     let remote: JQueryAjaxSettings = {  
       url: `${form.attr('action')}?validate=${elementName}`,  
       type: "post",  
       async: true,  
       contentType: 'application/json',  
       beforeSend: (xhr, setting) => {  
         setting.data = getData();  
       },  
       dataFilter: (response) => {  
         if ($.parseJSON(response) == true) {  
           return response;  
         }  
         var data = $.parseJSON(response);  
         var validator: any = $(form).validate();  
         validator.invalid[elementName] = data[elementName] != undefined;  
         return JSON.stringify(data[elementName] != undefined ? data[elementName] : true);  
       }  
     };  
     return remote;  
   }  
   RegisterJqueryValidation(formSelector: string) {  
     let form = $(formSelector);  
     $.ajax({  
       url: form.attr('action') + '?validation=jquery',  
       data: '{}',  
       contentType: 'application/json',  
       method: 'post',  
       success: (validationRule: JQueryValidation.ValidationOptions) => {  
         if (validationRule != undefined && validationRule.rules != undefined) {  
           validationRule.errorPlacement = (error, element) =>  
             this.AppendError(form, error, element);  
           validationRule.success = (label, input) =>  
             label.empty();  
           $.each(validationRule.rules, (prop: any) => {  
             if (validationRule.rules[prop].hasOwnProperty('remote')) {  
               validationRule.rules[prop].remote = () => this.RemoteValidation(prop, form);  
             }  
           });  
           $.validator.setDefaults({ ignore: '' });  
           var validator = form.validate(validationRule);  
         }  
       }  
     });  
   }  
 }  
 export { FormValidation }  

Usage

Form.ts was explained in the earlier blog post, but please refer to Form.ts in case of any confusion. The function RegisterJqueryValidation would take care of everything.

 this.Validation.RegisterJqueryValidation(this.ContactViewOption.FormSelector);  
 this.EventHelper.RegisterClickEvent(  
   this.ContactViewOption.SubmitButtonSelector,  
   (evt, selector) => {  
     this.FormHelper.SubmitForm({  
       Source: {  
         ButtonEvent: evt  
       },  
       OnPostSuccessResult: (data) => {  
         console.log('Submitted successfully.');  
       }  
     });  
   });  




Comments

Post a Comment

Popular posts from this blog

Elegantly dealing with TimeZones in MVC Core / WebApi

In any new application handling TimeZone/DateTime is mostly least priority and generally, if someone is concerned then it would be handled by using DateTime.UtcNow on codes while creating current dates and converting incoming Date to UTC to save on servers. Basically, the process is followed by saving DateTime to UTC format in a database and keep converting data to native format based on user region or single region in the application's presentation layer. The above is tedious work and have to be followed religiously. If any developer misses out the manual conversion, then that area of code/view would not work. With newer frameworks, there are flexible ways to deal/intercept incoming or outgoing calls to simplify conversion of TimeZones. These are steps/process to achieve it. 1. Central code for storing user's state about TimeZone. Also, central code for conversion logic based on TimeZones. 2. Dependency injection for the above class to be able to use global

Using Redis distributed cache in dotnet core with helper extension methods

Redis cache is out process cache provider for a distributed environment. It is popular in Azure Cloud solution, but it also has a standalone application to operate upon in case of small enterprises application. How to install Redis Cache on a local machine? Redis can be used as a local cache server too on our local machines. At first install, Chocolatey https://chocolatey.org/ , to make installation of Redis easy. Also, the version under Chocolatey supports more commands and compatible with Official Cache package from Microsoft. After Chocolatey installation hit choco install redis-64 . Once the installation is done, we can start the server by running redis-server . Distributed Cache package and registration dotnet core provides IDistributedCache interface which can be overrided with our own implementation. That is one of the beauties of dotnet core, having DI implementation at heart of framework. There is already nuget package available to override IDistributedCache i

Making FluentValidation compatible with Swagger including Enum or fixed List support

FluentValidation is not directly compatible with Swagger API to validate models. But they do provide an interface through which we can compose Swagger validation manually. That means we look under FluentValidation validators and compose Swagger validator properties to make it compatible. More of all mapping by reading information from FluentValidation and setting it to Swagger Model Schema. These can be done on any custom validation from FluentValidation too just that proper schema property has to be available from Swagger. Custom validation from Enum/List values on FluentValidation using FluentValidation.Validators; using System.Collections.Generic; using System.Linq; using static System.String; /// <summary> /// Validator as per list of items. /// </summary> /// <seealso cref="PropertyValidator" /> public class FixedListValidator : PropertyValidator { /// <summary> /// Gets the valid items /// <

Handling JSON DateTime format on Asp.Net Core

This is a very simple trick to handle JSON date format on AspNet Core by global settings. This can be applicable for the older version as well. In a newer version by default, .Net depends upon Newtonsoft to process any JSON data. Newtonsoft depends upon Newtonsoft.Json.Converters.IsoDateTimeConverter class for processing date which in turns adds timezone for JSON data format. There is a global setting available for same that can be adjusted according to requirement. So, for example, we want to set default formatting to US format, we just need this code. services.AddMvc() .AddJsonOptions(options => { options.SerializerSettings.DateTimeZoneHandling = "MM/dd/yyyy HH:mm:ss"; });

Kendo MVC Grid DataSourceRequest with AutoMapper

Kendo Grid does not work directly with AutoMapper but could be managed by simple trick using mapping through ToDataSourceResult. The solution works fine until different filters are applied. The problems occurs because passed filters refer to view model properties where as database model properties are required after AutoMapper is implemented. So, the plan is to intercept DataSourceRequest  and modify names based on database model. To do that we are going to create implementation of  CustomModelBinderAttribute to catch calls and have our own implementation of DataSourceRequestAttribute from Kendo MVC. I will be using same source code from Kendo but will replace column names for different criteria for sort, filters, group etc. Let's first look into how that will be implemented. public ActionResult GetRoles([MyDataSourceRequest(GridId.RolesUserGrid)] DataSourceRequest request) { if (request == null) { throw new ArgumentNullExce

Trim text in MVC Core through Model Binder

Trimming text can be done on client side codes, but I believe it is most suitable on MVC Model Binder since it would be at one place on infrastructure level which would be free from any manual intervention of developer. This would allow every post request to be processed and converted to a trimmed string. Let us start by creating Model binder using Microsoft.AspNetCore.Mvc.ModelBinding; using System; using System.Threading.Tasks; public class TrimmingModelBinder : IModelBinder { private readonly IModelBinder FallbackBinder; public TrimmingModelBinder(IModelBinder fallbackBinder) { FallbackBinder = fallbackBinder ?? throw new ArgumentNullException(nameof(fallbackBinder)); } public Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } var valueProviderResult = bindingContext.ValueProvider.GetValue(bin

Kendo MVC Grid DataSourceRequest with AutoMapper - Advance

The actual process to make DataSourceRequest compatible with AutoMapper was explained in my previous post  Kendo MVC Grid DataSourceRequest with AutoMapper , where we had created custom model binder attribute and in that property names were changed as data models. In this post we will be looking into using AutoMapper's Queryable extension to retrieve the results based on selected columns. When  Mapper.Map<RoleViewModel>(data)  is called it retrieves all column values from table. The Queryable extension provides a way to retrieve only selected columns from table. In this particular case based on properties of  RoleViewModel . The previous approach that we implemented is perfect as far as this article ( 3 Tips for Using Telerik Data Access and AutoMapper ) is concern about performance where it states: While this functionality allows you avoid writing explicit projection in to your LINQ query it has the same fatal flaw as doing so - it prevents the query result from

OpenId Authentication with AspNet Identity Core

This is a very simple trick to make AspNet Identity work with OpenId Authentication. More of all both approach is completely separate to each other, there is no any connecting point. I am using  Microsoft.AspNetCore.Authentication.OpenIdConnect  package to configure but it should work with any other. Configuring under Startup.cs with IAppBuilder app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme, LoginPath = new PathString("/Account/Login"), CookieName = "MyProjectName", }) .UseIdentity() .UseOpenIdConnectAuthentication(new OpenIdConnectOptions { ClientId = "<AzureAdClientId>", Authority = String.Format("https://login.microsoftonline.com/{0}", "<AzureAdTenant>"), ResponseType = OpenIdConnectResponseType.IdToken, PostLogoutRedirectUri = "<my website url>",

Data seed for the application with EF, MongoDB or any other ORM.

Most of ORMs has moved to Code first approach where everything is derived/initialized from codes rather than DB side. In this situation, it is better to set data through codes only. We would be looking through simple technique where we would be Seeding data through Codes. I would be using UnitOfWork and Repository pattern for implementing Data Seeding technique. This can be applied to any data source MongoDB, EF, or any other ORM or DB. Things we would be doing. - Creating a base class for easy usage. - Interface for Seed function for any future enhancements. - Individual seed classes. - Configuration to call all seeds. - AspNet core configuration to Seed data through Seed configuration. Creating a base class for easy usage public abstract class BaseSeed<TModel> where TModel : class { protected readonly IMyProjectUnitOfWork MyProjectUnitOfWork; public BaseSeed(IMyProjectUnitOfWork MyProjectUnitOfWork) { MyProject

MongoDB navigation property or making it behave as ORM in .Net

This is an implementation to make models to have  navigation properties work like ORM does for us. What actually happens in ORM to make navigation properties work? Entity Framework has proxy classes implementation to allow lazy loading and eager loading implementation. While creating proxy classes it also changes definitions for actual classes to make navigation properties work to get values based on Model's for navigation properties. Most of ORMs work in same fashion like Telerik DataAccess has enhancer tool which changes class definition at compile time to enable navigation properties. In this implementation, we would retain the original class but we would have extension methods to allow initializing properties to have navigation proprieties work. Let's first create desire model on which we need to implement. I am picking up simple one-to-many relationship example from Person to Address. public class Person { public int PersonId { get; set; }