Monday, March 25, 2019

RegEx: regular expression to copy fields with same name and data type from an object

Often time we need to copy values for objects having same property names and data types. Sometimes this can be done efficiently using libraries such as AutoMapper. However I have found it to be cumbersome to use it and also it adds lot of testing issues like explained at

https://softwareengineering.stackexchange.com/questions/167250/am-i-wrong-in-thinking-that-needing-something-like-automapper-is-an-indication-o

https://cezarypiatek.github.io/post/why-i-dont-use-automapper/

Instead I personally prefer plain old code and have found some useful shortcuts to reduce the efforts.

1) The following regex can be used in Find and Replace window to copy values between two such objects

Find:       [a-zA-Z]* [a-zA-Z]*[\?]? ([a-zA-Z]* ){ get; set; }
Replace:  $1 = SourceObj.$1,

2) Another regex
Find: public (?:virtual )?(?:\w+\<)?\w+\??(?:\>)? (\w+) { get; set; }
Replace: this.$1 = obj.$1;

2) Use custom reflection method as explained at

https://www.pluralsight.com/guides/property-copying-between-two-objects-using-reflection

Although end result by this method is similar to that of AutoMapper, a very big advantage is since we have entire source code in our project, it is easier to debug or add custom functionality to it.

Tuesday, March 19, 2019

Visual studio enable dot net framework source debugging

To enable dot net framework source debugging, enable this option as explained in the following post, and if for some reason it does not work, then export current settings, reset settings and re-import the saved settings.

https://stackoverflow.com/questions/40320257/cannot-find-cs-files-for-debugging-net-source-code

SharePoint Provider Hosted App - SecurityTokenException: Invalid issuer or signature

More often than not, this error simply means that SharePoint client secrets are incorrect. A detailed explanation and steps to resolve this error can be found at 

https://kirkbarrett.wordpress.com/2018/01/19/provider-hosted-app-error-invalid-issuer-or-signature-persists-after-update/

If we are absolutely sure that the client secrets are correct, then a possible reason for this error is mismatch between datetime zones of SharePoint online and Azure servers. Read more information at https://blogs.technet.microsoft.com/sharepointdevelopersupport/2015/03/11/provider-hosted-app-fails-on-spo/

Some solutions for this problem are listed below


  1. If the website is hosted in an azure server, wait for eight hours for the server to be in sync
  2. Remove service principle using Remove-MsolServicePrincipal powershell command in MSOnline Powershell. More information can be found at https://techcommunity.microsoft.com/t5/SharePoint-Developer/Invalid-issuer-o-signature-error-in-SPO-Provider-Hosted-AddIns/m-p/165697#M4444
  3. Use HostedAppHostNameOverride in web.config as described at https://www.credera.com/blog/technology-insights/microsoft-solutions/sharepoint-2013-provider-hosted-applications-windows-azure-paas/ 



Thursday, February 21, 2019

SharePoint Provider hosted app: Invalid length for a Base-64 char array exception

This error means the string which we are trying to convert to base 64 encoding does not have enough length. More explanation is available at https://stackoverflow.com/questions/12962992/invalid-length-for-a-base-64-char-array-exception.

To  fix this error, append '=' character to the string till it becomes divisible by four.

In a SharePoint provider hosted app, this generally means the client secret (or secondary client secret) needs to be appended as above.

Tuesday, February 12, 2019

View all model state errors while debugging

If we are in break mode while debugging an MVC controller event,  and want to view all ModelState errors, the following command can show all error messages in immediate window:


ModelState.Where(x => x.Value.Errors.Count>0).SelectMany((x, i)=> x.Value.Errors.Select(y=> "Key: " + x.Key + ", Error: " + y.ErrorMessage)).ToList()

Thursday, February 7, 2019

Simplistic Custom Sign In in MVC


To implement a very simple authentication in MVC where authentication is done using values stored in web.config files follow these steps.

1)     Create a folder Identity and create classes CustomSignInManager, CustomUser, CustomUserManager and CustomUserStore



CustomSignInManager class has the following code:

public class CustomSignInManager : SignInManager<CustomUser, string>
    {
        public CustomSignInManager(CustomUserManager userManager, IAuthenticationManager authenticationManager)
            : base(userManager, authenticationManager)
        {

        }

        public static CustomSignInManager Create(IdentityFactoryOptions<CustomSignInManager> options, IOwinContext context)
        {
            return new CustomSignInManager(context.GetUserManager<CustomUserManager>(), context.Authentication);
        }

        public override Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout)
        {
            //return base.PasswordSignInAsync(userName, password, isPersistent, shouldLockout);
            var signInStatus = SignInStatus.Failure;
            if (ConfigurationManager.AppSettings["UserName"].Trim().ToLower() == userName.Trim().ToLower() && ConfigurationManager.AppSettings["Password"] == password)
            {
                signInStatus = SignInStatus.Success;
            }
            return Task.FromResult(signInStatus);
        }
    }


CustomUser class has the following code:

public class CustomUser : IUser<string>
    {
        public string Id { get; set; }

        public string UserName { get; set; }

        public string UserRole { get; set; }
    }

CustomUserManager class has the following code:


public class CustomUserManager : UserManager<CustomUser>
    {
        public CustomUserManager(IUserStore<CustomUser> store) : base(store)
        {
        }

        public static CustomUserManager Create()
        {
            var manager = new CustomUserManager(new CustomUserStore());
            return manager;
        }
    }



CustomUserStore class has the following code:


public class CustomUserStore : IUserStore<CustomUser>
    {
        public CustomUserStore()
        {
            //this.database = new CustomDbContext();
        }

        public void Dispose()
        {
            //this.database.Dispose();
        }

        public Task CreateAsync(CustomUser user)
        {
            // TODO
            throw new NotImplementedException();
        }

        public Task UpdateAsync(CustomUser user)
        {
            // TODO
            throw new NotImplementedException();
        }

        public Task DeleteAsync(CustomUser user)
        {
            // TODO
            throw new NotImplementedException();
        }

        public async Task<CustomUser> FindByIdAsync(string userId)
        {
            await Task.Delay(0);
            return new CustomUser();
        }

        public async Task<CustomUser> FindByNameAsync(string userName)
        {
            await Task.Delay(0);
            return new CustomUser();
        }
    }


Add a property CustomSignInManager to AccountsController

public CustomSignInManager CustomSignInManager
        {
            get { return _signInManager ?? HttpContext.GetOwinContext().Get<CustomSignInManager>(); }
            private set { _signInManager = value; }
        }

Login method in AccountsController class has the following code:


if (!ModelState.IsValid)
            {
                return View(model);
            }
            //Authenticate the user
            var result = await CustomSignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
            switch (result)
            {
                case SignInStatus.Success:
                    var user = new CustomUser() {Id= "1", UserName = model.Email };
                    await CustomSignInManager.SignInAsync(user, model.RememberMe, false);
                    return RedirectToLocal(returnUrl);
                case SignInStatus.LockedOut:
                    return View("Lockout");
                case SignInStatus.RequiresVerification:
                    return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
                case SignInStatus.Failure:
                default:
                    ModelState.AddModelError("", "Invalid login attempt.");
                    return View(model);
            }

MVC BeginForm with querystring

For a provider hosted app in SharePoint any request to the server must preserve standard SharePoint tockens in query string. I have found a blog that explains how to achieve similar functionality in plain MVC
https://www.danylkoweb.com/Blog/quick-tip-querystrings-with-aspnet-mvc-beginform-FO

In practise, instead of adding another BeginFormQueryString extension to Html object I only copied the extension method on query string

public static RouteValueDictionary ToRouteValueDictionary(this NameValueCollection collection)
        {
            var routeValues = new RouteValueDictionary();
            foreach (string key in collection)
            {
                routeValues[key] = collection[key];
            }
            return routeValues;

        }

And then added the routevalues to OOB BeginForm method like so:



@using (@Html.BeginForm("myAction", "myController", Request.QueryString.ToRouteValueDictionary(), FormMethod.Post))
{

Regex Email validation in c# dot net core

 Use this regex /^_?[a-zA-Z0-9]([a-zA-Z0-9]*[._+-])*[a-zA-Z0-9_]+@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.(?!-)[A-Za-z0-9-]{1,63}(?<!-))*\.[A-...