Saturday, November 26, 2016

LINQ Performance pitfall instead a Pit stop!

I would rather named the title as Linq performance pit stop. Anyway in the interest of time, here is, one of the good examples of how we can start with something very trivial linq query to something convoluted one. Always remember the query in memory or on fly comes with its own pros and cons. One must understand how to use them and when. I took this particular example from pluralsight and thought it to be worth sharing.

Courtesy
https://app.pluralsight.com/library/courses/linq-more-effective/table-of-contents

by Mark Heath.

Slow
var longest=books.First(b => b.Pages==books.Max(a => a.Pages));

Better
var mostBooks= books.Max(a => a.Pages);
var longest=books.First(b => b.Pages == mostBooks);

Best
var longest= books.MaxBy(p => p.Pages);

Hope this useful.


Wednesday, September 28, 2016

Check field Template Source value using sitecore powershell SPE

Use Case :Review

Suppose we have a custom rich text  and it has been sourced with custom component or css .This is added when developer creates template field in data template. The below code snippet can be used as part of your review and refactoring process to clean up sitecore items. 

Here is the quick pick..Check Source field of rich text is empy or null.

$targetTemplate    = Get-item 'master:/sitecore/templates/User Defined/Common/Data';
$templateFilter    = Get-Item "master:/sitecore/templates/System/Templates/Template Field";


Get-ChildItem $targetTemplate.FullPath -recurse 
| Where-Object { $_.TemplateName -eq $templateFilter.Name -and $_.type -eq 'Rich Text' -and $_["source"] -eq ''} 
| ForEach-Object {
    $_.Name
    $_.Type
    $_["Source"] 
}

Reference


http://sitecore.stackexchange.com/questions/203/unable-to-get-value-of-source-using-spe

Add base template using sitecore powershell extension

Use Case

It might happen you may have to add base template to all your user defined template. Below is the script to help doing this. I used Adam's code as reference to get this working. During development we could have reach to a stage where we end up with lot of user defined templates and at some point we review and refactor template to have uniform approach. One of the script below can come handy to save some of our refactoring time.

# Add base template programmatically


$targetTemplate    = Get-item 'master:/sitecore/templates/User Defined/Common/Data';
$templateFilter    = Get-Item "master:/sitecore/templates/System/Templates/Template";
$templateToBeAddAsBaseTemplate     = Get-Item 'master:/sitecore/templates/User Defined/Common/Data/Carousel'


Get-ChildItem $targetTemplate.FullPath -recurse | Where-Object { $_.TemplateName -eq $templateFilter.Name -and $_.Name -eq "promo"} | ForEach-Object {
    if(-not ($_."__Base template" -match "$($templateToBeAddAsBaseTemplate.Id)")){
           #If not exist then add
         $_."__Base template" = "$( $_."__Base template")|$($templateToBeAddAsBaseTemplate.Id)" 
    }
}

Reference

http://stackoverflow.com/questions/18990973/append-base-template-to-template-using-sitecore-powershell

Add base template using sitecore powershell extension

Use Case

It might happen you may have to add base template to all your user defined template. Below is the script to help doing this. I used Adam's code as reference to get this working. During development we could have reach to a stage where we end up with lot of user defined templates and at some point we review and refactor template to have uniform approach. One of the script below can come handy to save some of our refactoring time.



$targetTemplate    = Get-item 'master:/sitecore/templates/User Defined/Common/Data';
$templateFilter    = Get-Item "master:/sitecore/templates/System/Templates/Template";
$templateToBeAddAsBaseTemplate     = Get-Item 'master:/sitecore/templates/User Defined/Common/Data/Carousel'


Get-ChildItem $targetTemplate.FullPath -recurse | Where-Object { $_.TemplateName -eq $templateFilter.Name -and $_.Name -eq "promo"} | ForEach-Object {
    if(-not ($_."__Base template" -match "$($templateToBeAddAsBaseTemplate.Id)")){
           #If not exist then add
         $_."__Base template" = "$( $_."__Base template")|$($templateToBeAddAsBaseTemplate.Id)" 
    }
}

Reference

http://stackoverflow.com/questions/18990973/append-base-template-to-template-using-sitecore-powershell

Monday, September 12, 2016

Sitecore CMS Active Directory Module and LDAP integration.

Introduction

I'm not going to reinvent the wheel. The idea is to help fellow developer to troubleshoot and few show stoppers that can come as a problem during integration of ldap to sitecore AD Module. I just try to put my experience altogether so as to address some issues and useful tool that can help you get started.

Use Cases

Enabling LDAP authentication in content authoring aka CMS system will provide additional level of security access control in place. This will ensure limited access and no misuse even we have CMS -CA accessible only to internal network non accessible publicly.

Assumption:

You've LDAP /AD account in place. Say. There is dev, test and prod group account created in AD. To each of these group set of AD users are assigned. Each of these group must be assigned with Service account say sv_dev_user.and so on. The Service account act as primary source of authentication to allow any external system seek access to AD group. Later it will enable set of users to access application or system.

  • Development/Test/Production Environment
    • AD GROUP 
      • AD User(s) 
      • One Service Account
Note : The service account is one that is used by sitecore AD Module.

This is used by one of the sitecore configuration.
add name="customAD" type="LightLDAP.SitecoreADMembershipProvider" connectionStringName="LDAPConnString" applicationName="sitecore" minRequiredPasswordLength="1" minRequiredNonalphanumericCharacters="0" requiresQuestionAndAnswer="false" requiresUniqueEmail="false"
connectionUsername="[Enter SERVICE Account User Name]" connectionPassword="[Enter Password]" connectionProtection="Secure" attributeMapUsername="sAMAccountName" enableSearchMethods="true"

***Facts About:AD Service Account Vs AD User Account

In certain scenarios we have systems running under AD Credentials (i.e. under a Service Account). These Service Accounts are created in exactly the same way as user accounts; the only difference being the name and description. A few things have been done to make a distinction between the two account types (e.g. which OU the account is in, whether "password never expires" is enabled, if "service account" is in the description), but there's no one rule which can be applied to everything to clearly distinguish between the two.

How to get Started?

Follow this blog , this more precise and easy steps to start with.

Troubleshooting?  

1. OU Group wrongly used in LDAP Connection string?

Always look at the right form of LDAP connection string. 99% of the cases Ldap connection is wrongly used in terms of OU group. There may be nested OU group. The best way to figure out OU group is use LDAPAdmin.exe. Check download section.

Connect LdapAdmin with service account userId and pwd. Once connected to LDAPAdmin try to locate AD Group and take one member(user) of that group and check its property . You can easily find the right hierarchy of OU aswell as right OU group.

2. Check LDAP Port in Ldap connection string?

  • Port 389(used for ldap browsing) vs 636 (secure)
add name="LDAPConnString" connectionString="LDAP://Mydomain:389/OU=SomePrimaryOU,OU=SomeChildOU,DC=Mydomain,DC=myCompany,DC=com,DC=au"

3. Always verify connectivity of LDAP AD account from server

One way to check directly using ldapAdmin.exe or you can check through this simple code base.

using System.DirectoryServices;
 
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            //mycompany.com.au
            var entry = new DirectoryEntry(@"LDAP://yourdomain:636/OU=primary,OU=secondary,DC=yourdomain,DC=mycompany,DC=com,DC=au", "Active directory user name","password");

            var search = new DirectorySearcher(entry);
            SearchResult result = search.FindOne();  

      }
      }
}

4. Sitecore Provider Status

                                       http://mydemo/sitecore/admin/providerstatus.aspx


5. User Manager : AD User Import Status Successful!


6. Domain: Check CustomAD Domain appeared!


7. Role Manager: Assign Sitecore Role to User

Make sure Sitecore Client Role is assigned to AD users.


8. Finally Login with AD user account

Ensure you using the domain name that you added as one of the membership provider. say you got domain name mydomain and you have used membership provider as below. Then you should login with customAD\UserId

add name="customAD" type="LightLDAP.SitecoreADMembershipProvider" connectionStringName="LDAPConnString" applicationName="sitecore" 

Downloads

References

Azure Cloud

Friday, August 26, 2016

Sitecore MVC Session handling

It all started when we had session timeout and it resulted in error page. Yes session checks were not handled properly.All I was looking to solve this trivial problem with quick code fix but it turn out to be a bit of research again. This is indeed a sitecore world, where every request surface with filters, route and action. Each request is fragmented as we had controller rendering to serve all our dynamic data and transaction commits.

****Register Global Filter to check for session. 
This will not work as there are some workflow pages which are not session or authorization dependent. It bit the purpose.

Next thought of handling this in SessionEnd pipeline.
GlobalFilters.Filters.Add(new SessionExpireAttribute());

****SessionEnd Pipeline Response.Redirect and Server.Transfer will not work!

Session_End is fired internally by the server, based on an internal timer. And thus there is no HttpRequest associted when that happens. That is why Response.Redirect or Server.Transfer does not make sense and will not work. 

**** SessionEnd is not fired if session mode is outproc.
It will be fired only when using session mode set as inproc or Custom.

****Solution: Not elegant but it worked.
Let the session timeout and then eventually it will be send to the path of common error handling module where we can take appropriate action to redirect it to login page or some information page to notify user about session timeout.
 
****Post Session Timeout-  User action server side event.
****Post Session Timeout- User action cliend side event fired.(Check for IsAjaxRequest)
 public class RenderingControllerExceptionProcessor : ExceptionProcessor
{  
    public override void Process(ExceptionArgs args)
    {
            var userContext = SessionService.Get();
            if (userContext == null)
                if (args.ExceptionContext.HttpContext.Request.IsAjaxRequest())
                {
             //send something to response result.
                    args.ExceptionContext.Result = new JsonResult
                    {
                        Data = new
                        {
                            Error = "NotAuthorized",
                            LogOnUrl = "/login"
                        },
                        JsonRequestBehavior = JsonRequestBehavior.AllowGet
                    };
                }
                else
                {
                    WebUtil.RedirectToLoginPage();
                }
   }
}
OR
 private bool IsSessionExpired()
        {
            if (HttpContext.Current.Session != null)
            {
                
                if ( HttpContext.Current.Session.IsNewSession)
                {
                    //Note: Below code fails for Custom session.Works for Inproc
                    string cookieHeaders = HttpContext.Current.Request.Headers["Cookie"];

                    if ((null != cookieHeaders) &&
                       (cookieHeaders.IndexOf("ASP.NET_SessionId", StringComparison.Ordinal) >= 0))
                    
                        // IsNewSession is true, but session cookie exists,
                        // so, ASP.NET session is expired
                        return true;
                    
                }
                // Session is not expired and function will return false,
                // could be new session, or existing active session
                return false;
            }
            return false;
        }
The only solution is to handle through global application error event in sitecore rendering.


Always Consider these values when dealing with session:.

  1. Polling interval
  2. Compression
  3. Session type
  4. SlidingExpiration

Side Note:
If your worker process restarts and it causes your cache to clear, think of using agent to ping Keepalive.aspx.

Monday, August 22, 2016

How string Interpolation can simplify expression? No more string.format

Just this example 


Console.WriteLine($"When {condition} is true, {(condition ? "it's true!" : "It's False")}");

C# Simplify Dispose Object

public void RetireHenchman()
{
    var disposableMinion = Minion as IDisposable;
    if (disposableMinion != null)
        disposableMinion.Dispose();
    Minion = null;
}
 
The null coalescing operator can make this code more concise as well:
 
public void RetireHenchman()
{
    (Minion as IDisposable)?.Dispose();
    Minion = null;
}
 


Friday, August 19, 2016

MVC Filter Order

While dealing with MVC filters we got various options to tweak the flow of action method calls at local and global level of application execution, The best case scenario are as follows:-

1. Check authorization for each action call.
2. Check whether request is legitimate as if it is human or bot based
3. Check or validate model request
4. Check session timeout
5. Check request length

[Authorize]--AuthorizeAttribute


  • IsAuthorized
  • OnAuthorized

public class ProtectedApiAttribute : AuthorizeAttribute
    {
        public ProtectedApiAttribute()
        {
            // do nothing, _repository will be lazily instantiated
        }
 
 
        protected override bool IsAuthorized(HttpActionContext actionContext)
        {
            var methodAccess = string.Empty;
            if (actionContext.Request.Method == HttpMethod.Get) methodAccess = "Get";
            if (actionContext.Request.Method == HttpMethod.Post) methodAccess = "Post";
            if (actionContext.Request.Method == HttpMethod.Put) methodAccess = "Put";
            if (actionContext.Request.Method == HttpMethod.Delete) methodAccess = "Delete";
 
            var isAuthorised = false;
            if (HttpContext.Current.Session.Count > 0)
            {
                isAuthorised = true;
            }
            return isAuthorised;
        }
    }
}


Filters run in the following order:
  1. Authorization filters
  2. Action filters
  3. Response filters
  4. Exception filters
For example, authorization filters run first and exception filters run last. Within each filter type, the Order value specifies the run order. Within each filter type and order, the Scope enumeration value specifies the order for filters. This enumeration defines the following filter scope values (in the order in which they run):
  1. First
  2. Global
  3. Controller
  4. Action
  5. Last

Thursday, August 18, 2016

C# 6.0 Initialization Gotchas

class Program
    {
        static void Main(string[] args)
        {
           var container = new SomeContainer();
           var range = Enumerable.Range(0, 10);
           foreach (var item in range)
               container.SomeNumbersHasSolution.Add(item);
 
           Console.WriteLine(container.SomeNumbersHasSolution.Count);
 
           foreach (var item in range)
               container.SomeNumbersHasProblem.Add(item);
 
           Console.WriteLine(container.SomeNumbersHasProblem.Count);
 
           Console.Read();
       }
       public class SomeContainer
       {
           public IList SomeNumbersHasProblem => new List();
           public IList SomeNumbersHasSolution { get; } = new List();
 
       }



  • SomeNumberHasProblem---Result 0
  • SomeNumberHasSolution---Result 10