Compare Two (Hungarian) Names using Permutations and LEVENSTEIN

Let’s say we want to check for name existence in a database. However the user input might not be sanitized or in the same  order as the one in the database. The name might have spelling mistakes. Also the female name is sometimes adjusted to match the male name after mariage. This might pose an issue in identifying the man or woman.

There is a wide range of selection of a married name. Until about the 18th century noblewomen kept their names at marriage and their children received their father’s name; it became compulsory only under the reign of Joseph II). When Hungary was under Habsburg rule and became influenced by Western European traditions, women became known by their husbands’ names. So for example Szendrey Júlia, marrying Petőfi Sándor, became Petőfi Sándorné (the -né suffix approximately means “wife of”, and this is the Hungarian equivalent of “Mrs.” as in “Mrs. John Smith”). This was both the law and the tradition until the 1950s. During the Communist rule of Hungary, great emphasis was put upon the equality of women and men, and from that time, women could either choose to keep their maiden name or take that of their husband. Most women did the latter except for artists.

Now, the alternatives for a woman when she marries are as shown below (using the examples of Szendrey Júlia and Petőfi Sándor – Júlia and Sándor are their given names):

  • Júlia can keep her maiden name, as Szendrey Júlia (now very popular, especially among more-educated women).

  • Júlia gives up her name, adds the suffix -né to her husband’s full name, and will be called Petőfi Sándorné.

  • Júlia adds the suffix -né to her husband’s family name, adds her full name and will be called Petőfiné Szendrey Júlia.

  • Júlia adds the suffix -né to her husband’s full name, adds her full name and will be called Petőfi Sándorné Szendrey Júlia (less popular these days, because it is long to write).

  • Júlia takes her husband’s family name, keeps her given name “Júlia” and will be called Petőfi Júlia.

You can see thy this might be a problem.

 

This is my solution:

It uses code from here: Getting percentage of character match of 2 strings – MSSQL – SQL Server / Alternative to SOUNDEX

 

CREATE FUNCTION [dbo].[CompareTwoNames]
(
	@string1 NVARCHAR(2000),
	@string2 NVARCHAR(2000),
	@percentMatchThreshold INT  = 80,
	@withWordPermutation BIT = 0
)
RETURNS BIT
AS
BEGIN

	SET @string1 = REPLACE(@string1, 'DR.','')
	SET @string2 = REPLACE(@string2, 'DR.','')
	SET @string1 = LTRIM(RTRIM(@string1))
    SET @string2 = LTRIM(RTRIM(@string2))
    
    
	DECLARE @AreStringsEqual BIT
    
    SET @AreStringsEqual = 0
    
    SET @string1 = REPLACE(@string1,N'Ăś', 'Ö')    
    SET @string1 = REPLACE(@string1,N'Ăź', 'Ü')
    SET @string1 = REPLACE(@string1,N'Ĺą', 'Ü')
    SET @string1 = REPLACE(@string1,N'ĂĄ', 'Á')
    SET @string1 = REPLACE(@string1,N'ĂŠ', 'É')
    SET @string1 = REPLACE(@string1,N'Ăł','Ó')
    SET @string1 = REPLACE(@string1,N'Ă','Á')
    SET @string1 = REPLACE(@string1,N'Ĺ','O')
    SET @string1 = REPLACE(@string1,N'é','É' )
    SET @string1 = REPLACE(@string1, N'ó','Ó'   )    
    SET @string1 = REPLACE(@string1,N'Ăł','Ó')
    SET @string1 = REPLACE(@string1,N'Ăś','O' )
    SET @string1 = REPLACE(@string1, N'É','É'   )    
    SET @string1 = REPLACE(@string1,N'ù','Ü' )
    SET @string1 = REPLACE(@string1, N'è','É'  )
    
    
    
    
    
    SET @string2 = REPLACE(@string2,N'Ăś', 'Ö')    
    SET @string2 = REPLACE(@string2,N'Ăź', 'Ü')
    SET @string2 = REPLACE(@string2,N'Ĺą', 'Ü')
    SET @string2 = REPLACE(@string2,N'ĂĄ', 'Á')
    SET @string2 = REPLACE(@string2,N'ĂŠ', 'É')
    SET @string2 = REPLACE(@string2,N'Ăł','Ó')
     SET @string2 = REPLACE(@string2,N'Ă','Á')
    SET @string2 = REPLACE(@string2,N'Ĺ','O')
    SET @string2 = REPLACE(@string2,N'é','É' )
    SET @string2 = REPLACE(@string2, N'ó','Ó')    
     SET @string2 = REPLACE(@string2,N'Ăł','Ó')
    SET @string2 = REPLACE(@string2,N'Ăś','O' )
    SET @string2 = REPLACE(@string2, N'É','É')
     SET @string2 = REPLACE(@string2,N'ù','Ü' )
    SET @string2 = REPLACE(@string2, N'è','É'  )
    
    
    
    
    
    IF @string1 IS NULL AND @string2 IS NULL
		BEGIN
			SET @AreStringsEqual = 1			
			RETURN @AreStringsEqual
		END
		
	IF LEN( Ltrim(Rtrim(@string1))) = 0 AND LEN(Ltrim(Rtrim(@string2))) = 0
	BEGIN
		SET @AreStringsEqual = 1
		RETURN @AreStringsEqual 
	END
	
	IF  @string1 IS NULL AND LEN(Ltrim(Rtrim(@string2))) = 0
	BEGIN
		SET @AreStringsEqual = 1
		RETURN @AreStringsEqual 
	END
	
	IF LEN( Ltrim(Rtrim(@string1))) = 0 AND @string2 IS NULL
	BEGIN
		SET @AreStringsEqual = 1
		RETURN @AreStringsEqual 
	END
    
    --SET @string1= @string1 COLLATE SQL_Latin1_General_CP1_CI_AS
    -- SET @string2= @string2 COLLATE SQL_Latin1_General_CP1_CI_AS
     
     SET @string1 = dbo.RemoveAccents(@string1)
     SET @string2 = dbo.RemoveAccents(@string2)
     
     SET @string1 = UPPER(@string1)
     SET @string2 = UPPER(@string2)
    
    IF ( Ltrim(Rtrim(@string1 COLLATE Latin1_General_CI_AI)) = Ltrim(Rtrim(@string2 COLLATE Latin1_General_CI_AI)) ) 
      BEGIN
          SET @AreStringsEqual = 1
          GOTO JustBeforeReturn--RETURN @AreStringsEqual 
      END
      
    IF SOUNDEX(@string1) = SOUNDEX(@string2)
      BEGIN
          SET @AreStringsEqual = 1
          GOTO JustBeforeReturn--RETURN @AreStringsEqual 
      END
      
    DECLARE @percentageMatch INT
    SELECT @percentageMatch = [AstraHu].[dbo].[GetPercentageOfTwoStringMatching] (  @string1, @string2)   
    
    IF @percentageMatch >= @percentMatchThreshold
	  BEGIN
          SET @AreStringsEqual = 1
          GOTO JustBeforeReturn--RETURN @AreStringsEqual 
      END
      
    IF(@string1 LIKE '%' + @string2 +'%') OR (@string2 LIKE '%' + @string1 +'%')
	  BEGIN
          SET @AreStringsEqual = 1
          GOTO JustBeforeReturn--RETURN @AreStringsEqual 
      END
      

    IF @withWordPermutation = 1
		BEGIN
			DECLARE @namesWithWordPermutationTable TABLE
			(
				id INT NOT NULL IDENTITY(1,1),
				name NVARCHAR(500)				
			)
			INSERT INTO @namesWithWordPermutationTable
			SELECT name FROM [dbo].[GenerateNamePermutation](@string1)
			DECLARE @Index INT = 1, @MaxIndex INT
			SELECT @MaxIndex = COUNT(1) FROM @namesWithWordPermutationTable
			WHILE @Index <= @MaxIndex
				BEGIN
					SET @AreStringsEqual = [dbo].[CompareTwoNames]((SELECT TOP(1) name FROM @namesWithWordPermutationTable WHERE id = @Index) ,	@string2 ,	@percentMatchThreshold,	 0)
					IF @AreStringsEqual = 1
						BREAK
					SET @Index= @Index + 1
				END
		END
     
    JustBeforeReturn:
    DECLARE @string1ContainsFemaleName BIT = 0, @string2ContainsFemaleName BIT = 0
    IF (@string1 LIKE '%NÉ %') OR (@string1 LIKE '%NÉ') OR (@string1 LIKE '%NE %') OR (@string1 LIKE '%NE')
		SET @string1ContainsFemaleName = 1
	IF (@string2 LIKE '%NÉ %') OR (@string2 LIKE '%NÉ') OR (@string2 LIKE '%NE %') OR (@string2 LIKE '%NE')
		SET @string2ContainsFemaleName = 1
		
	IF(@string1ContainsFemaleName <> @string2ContainsFemaleName)
		SET @AreStringsEqual = 0
		
	IF @string1ContainsFemaleName = @string2ContainsFemaleName AND @string2ContainsFemaleName = 1
		BEGIN	
			DECLARE @string1WithoutFemaleForm VARCHAR(2000), @string2WithoutFemaleForm VARCHAR(2000)
			SET @string1WithoutFemaleForm =		REPLACE(
													REPLACE(
														REPLACE(@string1,'NÉ ',' ')
														,'NE '
														,' ')
														,'NE'
														,'');
			SET @string2WithoutFemaleForm =		REPLACE(
													REPLACE(
														REPLACE(@string2,'NÉ ',' ')
														,'NE '
														,' ')
														,'NE'
														,'');
			SET @AreStringsEqual = [dbo].[CompareTwoNames](@string1WithoutFemaleForm,	@string2WithoutFemaleForm ,	@percentMatchThreshold,	 @withWordPermutation)
		END
		
    RETURN @AreStringsEqual 

END

CREATE FUNCTION [dbo].[GenerateNamePermutation] 
(
	@words NVARCHAR(500)
)
RETURNS 
@returnTable TABLE 
(
	name NVARCHAR(500)
)
AS
BEGIN
	-- Fill the table variable with the rows for your result set
	DECLARE @separator CHAR(1)
    DECLARE @NameParts TABLE (
      PartId INT IDENTITY,
      part   NVARCHAR(50))
   -- DECLARE @words NVARCHAR(50)
    
    SET @separator = ' '
    
    ;WITH Parts(pn, start, finish)
         AS (SELECT 1,
                    1,
                    CHARINDEX(@separator, @words)
             UNION ALL
             SELECT pn + 1,
                    finish + 1,
                    CHARINDEX(@separator, @words, finish + 1)
             FROM   Parts
             WHERE  finish > 0)
    INSERT INTO @NameParts
                (part)
    SELECT TOP(5) SUBSTRING(@words, start, CASE
                                     WHEN finish > 0 THEN finish - start
                                     ELSE 50
                                   END)
    FROM   Parts
    
    DECLARE @tokencount INT
    
    SELECT @tokencount = COUNT(*)
    FROM   @NameParts;
    
    WITH Subsets
         AS (SELECT CAST(' ' + part AS NVARCHAR(MAX)) Permutation,
                    CAST(1 AS INT)                   AS Iteration
             FROM   @NameParts
             UNION ALL
             SELECT Permutation + ' ' + part AS Permutation,
                    Iteration + 1            AS Iteration
             FROM   Subsets s
                    JOIN @NameParts n
                      ON s.Permutation NOT LIKE '%' + n.part + '%')
    INSERT INTO @returnTable
    SELECT STUFF(Permutation, 1, 1, '') AS Perm
    FROM   SUBSETS
    WHERE  Iteration = @tokencount 
    
	RETURN 
END

Getting percentage of character match of 2 strings – MSSQL – SQL Server / Alternative to SOUNDEX

Let’s say I have a set of 2 words:

Alexander and Alecsander OR Alexander and Alegzander

Alexander and Aleaxnder, or any other combination. In general we are talking about human error in typing of a word or a set of words.

What I want to achieve is to get the percentage of matching of the characters of the 2 strings.

Ok, here is my solution so far:

SELECT  [dbo].[GetPercentageOfTwoStringMatching]('valentin123456'  ,'valnetin123456')

returns 86%

CREATE FUNCTION [dbo].[GetPercentageOfTwoStringMatching]
(
    @string1 NVARCHAR(100)
    ,@string2 NVARCHAR(100)
)
RETURNS INT
AS
BEGIN

    DECLARE @levenShteinNumber INT

    DECLARE @string1Length INT = LEN(@string1)
    , @string2Length INT = LEN(@string2)
    DECLARE @maxLengthNumber INT = CASE WHEN @string1Length > @string2Length THEN @string1Length ELSE @string2Length END

    SELECT @levenShteinNumber = [dbo].[LEVENSHTEIN] (   @string1  ,@string2)

    DECLARE @percentageOfBadCharacters INT = @levenShteinNumber * 100 / @maxLengthNumber

    DECLARE @percentageOfGoodCharacters INT = 100 - @percentageOfBadCharacters

    -- Return the result of the function
    RETURN @percentageOfGoodCharacters

END




-- =============================================     
-- Create date: 2011.12.14
-- Description: http://blog.sendreallybigfiles.com/2009/06/improved-t-sql-levenshtein-distance.html
-- =============================================

CREATE FUNCTION [dbo].[LEVENSHTEIN](@left  VARCHAR(100),
                                    @right VARCHAR(100))
returns INT
AS
  BEGIN
      DECLARE @difference    INT,
              @lenRight      INT,
              @lenLeft       INT,
              @leftIndex     INT,
              @rightIndex    INT,
              @left_char     CHAR(1),
              @right_char    CHAR(1),
              @compareLength INT

      SET @lenLeft = LEN(@left)
      SET @lenRight = LEN(@right)
      SET @difference = 0

      IF @lenLeft = 0
        BEGIN
            SET @difference = @lenRight

            GOTO done
        END

      IF @lenRight = 0
        BEGIN
            SET @difference = @lenLeft

            GOTO done
        END

      GOTO comparison

      COMPARISON:

      IF ( @lenLeft >= @lenRight )
        SET @compareLength = @lenLeft
      ELSE
        SET @compareLength = @lenRight

      SET @rightIndex = 1
      SET @leftIndex = 1

      WHILE @leftIndex <= @compareLength
        BEGIN
            SET @left_char = substring(@left, @leftIndex, 1)
            SET @right_char = substring(@right, @rightIndex, 1)

            IF @left_char <> @right_char
              BEGIN -- Would an insertion make them re-align?
                  IF( @left_char = substring(@right, @rightIndex + 1, 1) )
                    SET @rightIndex = @rightIndex + 1
                  -- Would an deletion make them re-align?
                  ELSE IF( substring(@left, @leftIndex + 1, 1) = @right_char )
                    SET @leftIndex = @leftIndex + 1

                  SET @difference = @difference + 1
              END

            SET @leftIndex = @leftIndex + 1
            SET @rightIndex = @rightIndex + 1
        END

      GOTO done

      DONE:

      RETURN @difference
  END 

 

The solution uses the Levenstein algorithm:

The Levenshtein distance is a string metric for measuring the difference between two sequences. Informally, the Levenshtein distance between two words is the minimum number of single-character edits (insertions, deletions or substitutions) required to change one word into the other. It is named after the Soviet mathematician Vladimir Levenshtein, who considered this distance in 1965.

Issue of RazorViewEngine caching and Layout location in .NET Core

 

 

I recently tried to implement the razor  email template explained by Scott Sauber

Tutorial here: https://scottsauber.com/2018/07/07/walkthrough-creating-an-html-email-template-with-razor-and-razor-class-libraries-and-rendering-it-from-a-net-standard-class-library/

                         

All fine and dandy until I had issues regarding the template file. Problem was that even though the file is in ‘~\bin\Debug\netcoreapp2.2’ it searches for the template in the root folder such as ‘rootfolder\Views\Shared\_Layout.cshtml’ and not in ‘rootfolder\bin\Debug\netcoreapp2.2\Views\Shared\_Layout.cshtml’.

 

This is most likely generated by the fact that I have the views as an embedded resource in a CLASS LIBRARY and not in a Web Api solution directly.

 

The weird part is that if you do not have the files in the root folder, you still get the CACHED Layout page.

The good part is that when you PUBLISH the solution, it flattens the solution so the VIEWS are in ROOT folder.

 

[Solution]

The solution seems to be in the Startup.cs folder.

Got my solution from here: https://stackoverflow.com/q/50934768/249895

//https://stackoverflow.com/q/50934768/249895
     services.Configure<Microsoft.AspNetCore.Mvc.Razor.RazorViewEngineOptions>(o => {
                o.ViewLocationFormats.Add("/Views/{0}" + Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine.ViewExtension);
                o.FileProviders.Add(new Microsoft.Extensions.FileProviders.PhysicalFileProvider(AppContext.BaseDirectory));
            });

After this, you can put your code like this:

var contentRootPath = _hostingEnvironment.ContentRootPath;
string executingAssemblyDirectoryAbsolutePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
string executingAssemblyDirectoryRelativePath = System.IO.Path.GetRelativePath(contentRootPath, executingAssemblyDirectoryAbsolutePath);

string executingFilePath = $"{executingAssemblyDirectoryAbsolutePath.Replace('\\', '/')}/Views/Main.cshtml";
string viewPath = "~/Views/Main.cshtml";
string mainViewRelativePath = $"~/{executingAssemblyDirectoryRelativePath.Replace('\\','/')}/Views/Main.cshtml";

var getViewResult = _viewEngine.GetView(executingFilePath: executingFilePath, viewPath: viewPath, isMainPage: true);

<!-- OR -->

var getViewResult = _viewEngine.GetView(executingFilePath: viewPath, viewPath: viewPath, isMainPage: true);

Getting relative path to `bin\Debug\netcoreapp2.2` in ASP.NET Core 2

If you need the relative path of the executing assemblies, you will probably need something like this: ‘~\bin\Debug\netcoreapp2.2’

 

I recently had to do this because I had to render a razor view in an ASP.NET Core 2 application.

Tutorial here: https://scottsauber.com/2018/07/07/walkthrough-creating-an-html-email-template-with-razor-and-razor-class-libraries-and-rendering-it-from-a-net-standard-class-library/

Issue was that I had  to overcome a bug(https://stackoverflow.com/a/56504181/249895) that needed the relative netcodeapp2.2 that the asemblied resided in.

Example:

var viewPath = ‘~/bin/Debug/netcoreapp2.2/Views/MainView.cshtml’;

_viewEngine.GetView(executingFilePath: viewPath , viewPath: viewPath , isMainPage: true);

Getting the `bin\Debug\netcoreapp2.2` can be achieved by using this code:

 

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
public class RenderingService : IRenderingService
{
        
	private readonly IHostingEnvironment _hostingEnvironment;
	public RenderingService(IHostingEnvironment hostingEnvironment)
	{
	_hostingEnvironment = hostingEnvironment;
	}

	public string RelativeAssemblyDirectory()
	{
		var contentRootPath = _hostingEnvironment.ContentRootPath;
		string executingAssemblyDirectoryAbsolutePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
		string executingAssemblyDirectoryRelativePath = System.IO.Path.GetRelativePath(contentRootPath, executingAssemblyDirectoryAbsolutePath);
		return executingAssemblyDirectoryRelativePath;
	}
}

Code Beautifier: http://hilite.me/

AutoFac AsyncRunner – Fix for Autofac not working with background tasks

Inspired from here:

https://stackoverflow.com/a/33830165/249895

Usage:

    public class ServiceModule :Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            
            builder.RegisterType<AutoFac.AsyncRunner>().As<AutoFac.IAsyncRunner>().SingleInstance();
        }
    }

private AutoFac.IAsyncRunner _asyncRunner;

public Controller(AutoFac.IAsyncRunner asyncRunner)
{
	
	_asyncRunner = asyncRunner;
}

public void Function()
{
	_asyncRunner.Run<IService>((cis) =>
   {
	   try
	   {
		  //do stuff
	   }
	   catch
	   {
		   // catch stuff
		   throw;
	   }
   });
}

Class:

public class AsyncRunner : IAsyncRunner
{
	private ILifetimeScope _lifetimeScope { get; set; }

	public AsyncRunner(ILifetimeScope lifetimeScope)
	{
		//Guard.NotNull(() => lifetimeScope, lifetimeScope);
		_lifetimeScope = lifetimeScope;
	}

	public Task Run<T>(Action<T> action)
	{
		Task.Factory.StartNew(() =>
		{
			using (var lifetimeScope = _lifetimeScope.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag))
			{
				var service = lifetimeScope.Resolve<T>();
				action(service);
			}
		});
		return Task.FromResult(0);
	}


}

HttpClient with Error Logging Handler and Retry Handler

Inspired from here: http://www.thomaslevesque.com/2016/12/08/fun-with-the-httpclient-pipeline/

 

Usage:

            var cookieContainer = new System.Net.CookieContainer();

            using (var httpClientHandler = new HttpClientHandler() { UseCookies = true, CookieContainer = cookieContainer })
            using (var errorLoggingHandler = new Handlers.LoggingHandler<AirWatchLogDto>(_httpGetLogger, httpClientHandler))
            //using (var retryHandler = new RetryHandler(errorLoggingHandler) { RetryCounterCount = retryCounterCount })
            using (var httpClient = new HttpClients.AirWatchCustomHeadersHttpClient(httpClientHandler, _appSettingsService))
            {
                HttpRequestMessage httpRequestMessage = new HttpRequestMessage(httpMethod, requestUri);

                if (httpMethod == HttpMethod.Post && postObject != null)
                {
                    string formattedJson = Newtonsoft.Json.JsonConvert.SerializeObject(postObject);
                    httpRequestMessage.Content = new StringContent(formattedJson, Encoding.UTF8, "application/json");
                }

                var httpReponseMessage = await httpClient.SendAsync(httpRequestMessage);
                string content = await httpReponseMessage.Content.ReadAsStringAsync();
                return httpReponseMessage;
            }

Retry Handler class:

    public class RetryHandler : DelegatingHandler
    {
        private int _retryCounterCount = 3;
        public int RetryCounterCount { get { return _retryCounterCount; } set { _retryCounterCount = value; } }

        public RetryHandler(HttpMessageHandler innerHandler) : base(innerHandler)
        {
        }

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            int counter = 0;
            while (true)
            {
                try
                {
                    counter++;                    
                    // base.SendAsync calls the inner handler
                    var response = await base.SendAsync(request, cancellationToken);
                    
                    if (counter >= RetryCounterCount)
                    {
                        return response;
                    }
                    if (response.StatusCode == HttpStatusCode.ServiceUnavailable)
                    {
                        // 503 Service Unavailable
                        // Wait a bit and try again later
                        await Task.Delay(5000, cancellationToken);
                        continue;
                    }

                    if (response.StatusCode == (HttpStatusCode)429)
                    {
                        // 429 Too many requests
                        // Wait a bit and try again later
                        await Task.Delay(1000, cancellationToken);
                        continue;
                    }

                    // Not something we can retry, return the response as is
                    return response;
                }
                catch (Exception ex) when (IsNetworkError(ex))
                {
                    if (counter >= RetryCounterCount)
                    {
                        throw;
                    }
                    // Network error
                    // Wait a bit and try again later
                    await Task.Delay(2000, cancellationToken);
                    continue;
                }
            }
        }

        private static bool IsNetworkError(Exception ex)
        {
            // Check if it's a network error
            if (ex is SocketException)
                return true;
            if (ex.InnerException != null)
                return IsNetworkError(ex.InnerException);
            return false;
        }
    }

Logging Handler:

    //http://www.thomaslevesque.com/2016/12/08/fun-with-the-httpclient-pipeline/
    public class LoggingHandler<T> : DelegatingHandler
    {
        private readonly IHttpClientEventLogger<T> _logger;

        public LoggingHandler(IHttpClientEventLogger<T> logger, HttpMessageHandler innerHandler) : base(innerHandler)
        {
            _logger = logger;
        }

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            try
            {
                _logger.MapRequest(request);
                
                var response = await base.SendAsync(request, cancellationToken);
                _logger.MapResponse(response);
                return response;
            }
            catch (Exception ex)
            {
                _logger.MapResponseException(ex);
                throw;
            }
            finally
            {
                _logger.LogEvent();
            }
        }
    }

    public class ErrorLoggingHandler<T> : DelegatingHandler
    {
        private readonly IHttpClientEventLogger<T> _logger;

        public ErrorLoggingHandler(IHttpClientEventLogger<T> logger, HttpMessageHandler innerHandler) : base(innerHandler)
        {
            _logger = logger;
        }

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            bool hasError = false;
            try
            {
                _logger.MapRequest(request);

                var response = await base.SendAsync(request, cancellationToken);
                
                return response;
            }
            catch (Exception ex)
            {
                hasError = true;
                _logger.MapResponseException(ex);
                throw;
            }
            finally
            {
                if(hasError)
                    _logger.LogEvent();
            }
        }
    }

Validating a view model after custom model binding

Adapted from here:
https://stackoverflow.com/questions/13684354/validating-a-view-model-after-custom-model-binding

Usage:

CustomModelBinderHelper.DoValidation(bindingContext, indexViewModel);

Implementation:

    public static class CustomModelBinderHelper
    {
        /// <summary>
        /// https://stackoverflow.com/a/22652195/249895
        /// </summary>
        /// <param name="bindingContext"></param>
        /// <param name="model"></param>
        public static void DoValidation(ModelBindingContext bindingContext, IValidatableObject model)
        {
            var validationResults = new HashSet<ValidationResult>();
            var isValid = Validator.TryValidateObject(model, new ValidationContext(model, null, null), validationResults, true);
            if (!isValid)
            {
                var resultsGroupedByMembers = validationResults
                    .SelectMany(_ => _.MemberNames.Select(
                         x => new
                         {
                             MemberName = x ?? "",
                             Error = _.ErrorMessage
                         }))
                    .GroupBy(_ => _.MemberName);

                foreach (var member in resultsGroupedByMembers)
                {
                    bindingContext.ModelState.AddModelError(
                        member.Key,
                        string.Join(". ", member.Select(_ => _.Error)));
                }
            }
        }
    }

 

To be noted that objects have to implment System.ComponentModel.DataAnnotations.IValidatableObject interface.

Kendo Mvc CustomKendoMvcExtensions. DataSourceRequest Filters in case of Datetime comparison. Don’t take time into consideration

Usage:

return Json(requestsList.ToCustomDataSourceResult(request));

Or:

public ActionResult GetList([CustomDataSourceRequest]DataSourceRequest request)

Implementation:

using Kendo.Mvc;
using Kendo.Mvc.Extensions;
using Kendo.Mvc.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web.Mvc;




namespace Kendo.Mvc.Extensions
{
    public static class CustomKendoMvcExtensions
    {
        /// <summary>
        /// Changes DataSourceRequest Filters in case of Datetime comparison. Does not take time into consideration
        /// //http://www.crowbarsolutions.com/ignoring-time-when-filtering-dates-in-telerik-kendo-grids/
        /// Adapted from above link.
        /// Uses CustomDataSourceRequestModelBinder.TransformFilterDescriptors function
        /// </summary>
        /// <param name="queryable"></param>
        /// <param name="request"></param>
        /// <returns></returns>
        public static DataSourceResult ToCustomDataSourceResult(this IQueryable queryable, DataSourceRequest request)
        {
            if (request.Filters != null && request.Filters.Count > 0)
            {

                ModelBinders.CustomDataSourceRequestModelBinder customModelBinder = new ModelBinders.CustomDataSourceRequestModelBinder();

                var transformedFilters = request.Filters.Select(customModelBinder.TransformFilterDescriptors).ToList();
                request.Filters = transformedFilters;
            }

            return queryable.ToDataSourceResult(request);
        }
    }
}


namespace Kendo.Mvc.UI
{
    //http://www.crowbarsolutions.com/ignoring-time-when-filtering-dates-in-telerik-kendo-grids/
    public class CustomDataSourceRequestAttribute : DataSourceRequestAttribute
    {
        public override IModelBinder GetBinder()
        {
            return new Kendo.Mvc.ModelBinders.CustomDataSourceRequestModelBinder();
        }
    }
}

namespace Kendo.Mvc.ModelBinders
{
    /// <summary>
    /// DateTime filtering is horribly unintuitive in Kendo Grids when a non-default (00:00:00) time is attached
    /// to the grid's datetime data. We use this custom model binder to transform the grid filters to return 
    /// results that ignore the attached time, leading to intuitive results that make users happy.
    /// 
    /// To use the code, substitute the [DataSourceRequest] attribute for [CustomDataSourceRequest] in your MVC controller
    /// </summary>
    public class CustomDataSourceRequestModelBinder : IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            // Get an instance of the original kendo model binder and call the binding method
            var baseBinder = new DataSourceRequestModelBinder();
            var request = (DataSourceRequest)baseBinder.BindModel(controllerContext, bindingContext);

            if (request.Filters != null && request.Filters.Count > 0)
            {
                var transformedFilters = request.Filters.Select(TransformFilterDescriptors).ToList();
                request.Filters = transformedFilters;
            }

            return request;
        }

        public IFilterDescriptor TransformFilterDescriptors(IFilterDescriptor filter)
        {
            if (filter is CompositeFilterDescriptor)
            {
                var compositeFilterDescriptor = filter as CompositeFilterDescriptor;
                var transformedCompositeFilterDescriptor = new CompositeFilterDescriptor { LogicalOperator = compositeFilterDescriptor.LogicalOperator };
                foreach (var filterDescriptor in compositeFilterDescriptor.FilterDescriptors)
                {
                    transformedCompositeFilterDescriptor.FilterDescriptors.Add(TransformFilterDescriptors(filterDescriptor));
                }
                return transformedCompositeFilterDescriptor;
            }
            if (filter is FilterDescriptor)
            {
                var filterDescriptor = filter as FilterDescriptor;
                if (filterDescriptor.Value is DateTime)
                {
                    var value = (DateTime)filterDescriptor.Value;
                    switch (filterDescriptor.Operator)
                    {
                        case FilterOperator.IsEqualTo:
                            //convert the "is equal to <date><time>" filter to a "is greater than or equal to <date> 00:00:00" AND "is less than or equal to <date> 23:59:59"
                            var isEqualCompositeFilterDescriptor = new CompositeFilterDescriptor { LogicalOperator = FilterCompositionLogicalOperator.And };
                            isEqualCompositeFilterDescriptor.FilterDescriptors.Add(new FilterDescriptor(filterDescriptor.Member,
                                FilterOperator.IsGreaterThanOrEqualTo, new DateTime(value.Year, value.Month, value.Day, 0, 0, 0)));
                            isEqualCompositeFilterDescriptor.FilterDescriptors.Add(new FilterDescriptor(filterDescriptor.Member,
                                FilterOperator.IsLessThanOrEqualTo, new DateTime(value.Year, value.Month, value.Day, 23, 59, 59)));
                            return isEqualCompositeFilterDescriptor;

                        case FilterOperator.IsNotEqualTo:
                            //convert the "is not equal to <date><time>" filter to a "is less than <date> 00:00:00" OR "is greater than <date> 23:59:59"
                            var notEqualCompositeFilterDescriptor = new CompositeFilterDescriptor { LogicalOperator = FilterCompositionLogicalOperator.Or };
                            notEqualCompositeFilterDescriptor.FilterDescriptors.Add(new FilterDescriptor(filterDescriptor.Member,
                                FilterOperator.IsLessThan, new DateTime(value.Year, value.Month, value.Day, 0, 0, 0)));
                            notEqualCompositeFilterDescriptor.FilterDescriptors.Add(new FilterDescriptor(filterDescriptor.Member,
                                FilterOperator.IsGreaterThan, new DateTime(value.Year, value.Month, value.Day, 23, 59, 59)));
                            return notEqualCompositeFilterDescriptor;

                        case FilterOperator.IsGreaterThanOrEqualTo:
                            //convert the "is greater than or equal to <date><time>" filter to a "is greater than or equal to <date> 00:00:00"
                            filterDescriptor.Value = new DateTime(value.Year, value.Month, value.Day, 0, 0, 0);
                            return filterDescriptor;

                        case FilterOperator.IsGreaterThan:
                            //convert the "is greater than <date><time>" filter to a "is greater than <date> 23:59:59"
                            filterDescriptor.Value = new DateTime(value.Year, value.Month, value.Day, 23, 59, 59);
                            return filterDescriptor;

                        case FilterOperator.IsLessThanOrEqualTo:
                            //convert the "is less than or equal to <date><time>" filter to a "is less than or equal to <date> 23:59:59"
                            filterDescriptor.Value = new DateTime(value.Year, value.Month, value.Day, 23, 59, 59);
                            return filterDescriptor;

                        case FilterOperator.IsLessThan:
                            //convert the "is less than <date><time>" filter to a "is less than <date> 00:00:00"
                            filterDescriptor.Value = new DateTime(value.Year, value.Month, value.Day, 0, 0, 0);
                            return filterDescriptor;

                        default:
                            throw new Exception(string.Format("Filter operator '{0}' is not supported for DateTime member '{1}'", filterDescriptor.Operator, filterDescriptor.Member));
                    }
                }
            }
            return filter;
        }
    }
}

Left side, vertical, scroll through menu

If you want to create a vertical menu that scrolls through the items as you scrolls through the page

 

HTML:

<nav id="leftMenu">
	<ul class="sidebar-nav nav leftMenuItems padding-0">
		<li><h4>Navigation Menu</h4></li>
		<li class="active">     <a id="menuItem202" href="#formSectionInstance_202">Menu Item 1</a> </li>
		<li><a id="menuItem203" href="#formSectionInstance_203">Menu Item 2<</a></li>
		<li><a id="menuItem204" href="#formSectionInstance_204">Menu Item 3<</a></li>
		<li><a id="menuItem208" href="#formSectionInstance_208">Menu Item 4<</a></li>
		<li><a id="menuItem206" href="#formSectionInstance_206">Menu Item 5<</a></li>
		<li><a id="menuItem207" href="#formSectionInstance_207_documentUpload">Menu Item 6<</a></li>
	</ul>
</nav>

 

Javascript:

function BindEventsToFormMenuItems()
{
	// Cache selectors
	var lastId,
		topMenu = $("#navbar"),
		leftMenu = $("#leftMenu"),
		topMenuHeight = topMenu.outerHeight() + 15,
		// All list items
		menuItems = leftMenu.find("a"),
		// Anchors corresponding to menu items
		scrollItems = menuItems.map(function ()
		{
			var item = $($(this).attr("href"));
			if (item.length) { return item; }
		});

	// Bind click handler to menu items
	// so we can get a fancy scroll animation
	menuItems.each(function (index) {
		$(this).click(function (e) {
			var href = $(this).attr("href"),
				offsetTop = href === "#" ? 0 : $(href).offset().top - topMenuHeight + 1;
			$('html, body').stop().animate({
				scrollTop: offsetTop
			}, 300);
			e.preventDefault();
			
			if ($('#menu-toggle').is(':visible'))
			{
				$("#menu-toggle").trigger("click");
			}

			menuItems.parent().removeClass("active");
			$(this).parent().addClass("active");
		});
	});

	// Bind to scroll
	$(window).scroll(function ()
	{
		// Get container scroll position
		var fromTop = $(this).scrollTop() + topMenuHeight + 50;

		// Get id of current scroll item
		var cur = scrollItems.map(function ()
		{
			if ($(this).offset().top < fromTop)
				return this;
		});
		// Get the id of the current element
		cur = cur[cur.length - 1];
		var id = cur && cur.length ? cur[0].id : "";

		if (lastId !== id)
		{
			lastId = id;
			// Set/remove active class
			menuItems
			  .parent().removeClass("active")
			  .end().filter("[href='#" + id + "']").parent().addClass("active");
		}
	});
}

SQL Server – make a copy of parent child rows

Let us say we have 2 tables that are in a parent-child relation.

We want to make a copy of the FormSectionInstance rows, but also copy and correlate the FormDetails rows.

This means copy rows from 2 tables and then set them up with the correct NEW corresponding IDs.

We could use a cursor, but a better alternative is the the MERGE INTO function.

We also create a TEMP transition table to hold the new VS old values. We use these values when we insert the CHILD values.

This way we can make the INSERT faster.

-- Copy the FormSectionInstance
DECLARE @FormSectionInstanceTable TABLE(OldFormSectionInstanceId INT, NewFormSectionInstanceId INT)

;MERGE INTO [dbo].[FormSectionInstance]
USING
(
	SELECT
		fsi.FormSectionInstanceId [OldFormSectionInstanceId]
		, @NewFormHeaderId [NewFormHeaderId]
		, fsi.FormSectionId
		, fsi.IsClone
		, @UserId [NewCreatedByUserId]
		, GETDATE() NewCreatedDate
		, @UserId [NewUpdatedByUserId]
		, GETDATE() NewUpdatedDate
	FROM [dbo].[FormSectionInstance] fsi
	WHERE fsi.[FormHeaderId] = @FormHeaderId 
) tblSource ON 1=0 -- use always false condition
WHEN NOT MATCHED
THEN INSERT
( [FormHeaderId], FormSectionId, IsClone, CreatedByUserId, CreatedDate, UpdatedByUserId, UpdatedDate)
VALUES( [NewFormHeaderId], FormSectionId, IsClone, NewCreatedByUserId, NewCreatedDate, NewUpdatedByUserId, NewUpdatedDate)
	
OUTPUT tblSource.[OldFormSectionInstanceId], INSERTED.FormSectionInstanceId
INTO @FormSectionInstanceTable(OldFormSectionInstanceId, NewFormSectionInstanceId);
	

-- Copy the FormDetail
INSERT INTO [dbo].[FormDetail]
	(FormHeaderId, FormFieldId, FormSectionInstanceId, IsOther, Value, CreatedByUserId, CreatedDate, UpdatedByUserId, UpdatedDate)
SELECT
	@NewFormHeaderId, FormFieldId, fsit.NewFormSectionInstanceId, IsOther, Value, @UserId, CreatedDate, @UserId, UpdatedDate
FROM [dbo].[FormDetail] fd
INNER JOIN @FormSectionInstanceTable fsit ON fsit.OldFormSectionInstanceId = fd.FormSectionInstanceId
WHERE [FormHeaderId] = @FormHeaderId