Wednesday, August 17, 2016

Hail, Architect- Don't just get swayed by the hype around new releases of Sitecore.

It's not just about sitecore or it's new releases or update which is announced to all out there. Its about plan it and time it well. Your stakeholder is dependent upon you. What you say is written on the stone. So be careful. Like all other releases of software or product there is cooling period for any product to get matured and get adopted to the ecosystem. What I meant is a time to make your product stand strong against all issues which may surface in production and can haunt you for a week. If you see in the past almost all product or software underwent changes due to fixes or unstable or may be success to certain degree or call it a flop show.

Having said this, cooling period is something one must consider while setting up expectation with stakeholder. Watch the space carefully. 

Just don't get swayed by the hype of features it comes with rather you face the reality with risk/issues the new release can offer in return.

Next time you hear something about releases with new feature .Hold your horses! 

Assess it well! Act Smart ! Sell while Deliver !


This is my opinion and may not concur with everyone.



Tuesday, August 16, 2016

How to get to the bottom of Sitecore Performance issues?

The idea is to give the quicklinks to gather data for performance issue analysis. Most of us may not be aware about the intrinsic .net framework and internal of CLR in details as we more focus on implementations and make use of features. This is where actual concept comes into play.


Debugging TOOL

1. DebugDiag

https://channel9.msdn.com/Shows/Defrag-Tools/Defrag-Tools-122-DebugDiag-Part-2
Performance counters
Proc Dump

2. Debugging Production Environment Windbg & SOS

https://channel9.msdn.com/Series/-NET-Debugging-Stater-Kit-for-the-Production-Environment/Windbg--SOS-03

3. Sons of Strike- SOS.dll

https://msdn.microsoft.com/en-us/library/bb190764(v=vs.110).aspx

If you really want to deep dive further refer Tezz Fernandez blogs and articles.

A long story short, if you are into critical production performance issue, sitecore support team may need your help to get certain process stats and data here are the few links that you must go through to nail down the issues.
https://blogs.msdn.microsoft.com/tess/

Some unanswered Questions?

Q.1  How to collect memory dumps using Task Manager ?
https://kb.sitecore.net/articles/758511

Q.2. How to collect memory dumps using ProcDump ?
https://kb.sitecore.net/articles/253710

Q.3. How to identify Sitecore process in Performance Monitor ?
https://kb.sitecore.net/articles/931689

Q.3. How to collect memory dumps using Managed GC Aware Dump ?
https://kb.sitecore.net/articles/284285

Q.4. How to collect memory dumps ?
https://kb.sitecore.net/articles/488758

Deep Containment Designs

var location = default(string);
if (vendor != null)
{
    if (vendor.ContactPerson != null)
    {
        if (vendor.ContactPerson.HomeAddress != null)
        {
            location = vendor.ContactPerson.HomeAddress.LineOne;
        }
    }
}

Now, using C# 6, this same idiom becomes much more readable:

var location = vendor?.ContactPerson?.HomeAddress?.LineOne;







Tuesday, August 9, 2016

Sitecore Database Internal -Items, VersionedFields and UnVersionedFields

These tables pretty much make sitecore CMS. Items and values stored in fields. Everything is just an ITEM.


SELECT [Id]
      ,[ItemId]
      ,[Language]
      ,[FieldId]
      ,[Value]
      ,[Created]
      ,[Updated]
      ,[DAC_Index]
  FROM [Sitecore_master].[dbo].[UnversionedFields]

SELECT [Id]
      ,[ItemId]
      ,[Language]
      ,[Version]
      ,[FieldId]
      ,[Value]
      ,[Created]
      ,[Updated]
      ,[DAC_Index]
  FROM [Sitecore_master].[dbo].[VersionedFields]

SELECT [ID]
      ,[Name]
      ,[TemplateID]
      ,[MasterID]
      ,[ParentID]
      ,[Created]
      ,[Updated]
      ,[DAC_Index]
  FROM [Sitecore_master].[dbo].[Items]

Monday, August 8, 2016

Capture Asp.net webservice traffic request when using with sitecore

I was doing bit of research playing around postman interceptor and fiddler but couldn't find the solution to view the calls made by sitecore.net to asp.net webservice that is been called as part of middle tier.

Even Redgate Ant profiler even could not able to capture such outgoing requests to other asp.net webservice. The idea was to assess the response time elapsed to get data from external webservice to our sitecore solution. We want to make sure what we developed in sitecore will be as optimum in terms of our design approach. We ensured most of the component is cached and rendered appropriately.

  • Doing this you can see performance profiling stats of outgoing request to external api , web service, wcf service etc.
  • You can see them available in Redgate Ants performance profiler and VS2015 n tier profiling.
  • Fiddler to give you all in and out of http requests.
 
Finally i was surfing through knowledge base of sitecore and happen to find this work around.
Note: The profiler will only capture requests made by System.Net.HttpWebRequest

Courtesy Sitecore
https://kb.sitecore.net/articles/017181



Solution






To configure the Fiddler tool to capture web service calls from an ASP.NET web site, you need to perform the following actions on your solution:
  1. In the Global.asax file, implement the following Application_Start method:
        public void Application_Start()
        {
            ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
        }
    
  2. In the Global.asax file, add the following import statements:
    <%@ Import Namespace="System.Net" %>
    <%@ Import Namespace="System.Net.Security" %>
  3. In the web.config file, configure the following XML under the section:
    <system .net>
      <defaultproxy enabled="true" usedefaultcredentials="true">
        <proxy autodetect="false" bypassonlocal="false" proxyaddress="http://127.0.0.1:8888" usesystemdefault="false">
      </proxy>

</configuration>


  • Enable Fiddler to capture the HTTPS traffic.

  • After the traffic is captured by Fiddler, it can be saved for further sharing and analysis.

    Saturday, July 23, 2016

    Sitecore Media Library like never before. Mind it before you leave its configuration unattended

    According to me this topic is mostly overlooked and realized when it is in post production environment. It isn't that the sitecore media library is simple to use perhaps it is the only area where the subject is realized when dealt with different scenario.

    Here is some of the scenarios I remember at top of my head.
    • Media is cached at client or end user browser
    • Media cached at server side
    • Media is uploaded in file system and database without proper instruction set to the end-users. Then comes the problem such as multi content delivery sync with one authoring server. Architecture decision. 
    • For multi-server environments, media must be propagated from the CMS server to the
    • content delivery server(s) using the Sitecore Staging module
    • AA Compliance site where image alt text is mandatory and content author dependency on alt text.etc.
    • Performance and security concerns around media library.
    • Performance hit if stored files in database as compared to disk. 

    Here is few important settings that are important to take before it is released to production.
    Locate these configuration in sitecore.config

    MEDIA - DISABLE FILE MEDIA
      <!--  MEDIA - DISABLE FILE MEDIA
                Enables or disables storage of media as files rather than database records.
                If true, user interfaces do not present options to store media as files.
                All files will be stored in the database, disregarding the value of the Media.UploadAsFiles setting.  
               
                Default value: false
          -->

        <setting name="Media.DisableFileMedia" value="false">

    MEDIA - CACHING ENABLED -Content delivery at SERVER Side

      <!--  MEDIA - CACHING ENABLED
                Indicates if caching of media files is enabled.
                Default value: true
          -->
        <setting name="Media.CachingEnabled" value="true" />

    MEDIA - MAX SIZE IN MEMORY to edit media content

       <!--  MEDIA - MAX SIZE IN MEMORY
                The maximum size of media to load into memory for processing (resizing etc.).
                Default value: 40MB
          -->

    MEDIA - MAX SIZE IN DATABASE to transfer and commit media in database

     <!--  MEDIA - MAX SIZE IN DATABASE
                The maximum allowed size of media intended to be stored in a database (binary blob).
                This value must be less than the ASP.NET httpRuntime.maxRequestLength setting.
                Default value: 500MB
          -->
        <setting name="Media.MaxSizeInDatabase" value="500MB" />

    MEDIA - AUTO SET ALT

     <!--  MEDIA - AUTO SET ALT
                Indicates if the Alt field on the Image template is set automatically to the file
                path when the image is uploaded. If false, the Alt field is blank.
          -->
         <setting name="Media.AutoSetAlt" value="false" />

    MEDIA BROWSER CACHED 

    ***The most important ones. Look into this configuration carefully. Read through.
    I'll give you the real time scenario. Just imagine website is developed and released to production. Everything is going fine and this website is big brand corporate facing website which is publicly available. Unfortunately there is media release that needs urgent update to important policy document without changing the media url as this media url is used everywhere in newsletter and corporate bible and so on. Now there is content author or business marketing team that wants to change the file content and upload with same file name immediately. 
    BOOM! .......................
    Despite all the efforts with proper publish and all the changes done correctly still the new file with same name and url is not reflecting in the content delivery website. The reason is... it is cached in client and server browser. Production support team is called for the rescue with P2 level raised as service incident request. 

    https://kb.sitecore.net/articles/218124

    Courtesy Sitecore.
    When new content is attached to an already published media item and the media item is published again, the content change might not be reflected in the browser immediately when you access the media using its URL.
    This happens because the media is cached in the browser for the time specified in the MediaResponse.MaxAge setting. If a browser has already accessed the media item, it may not perform any requests to the server and may retrieve the media item from its cache until the cached version expires.

     <!--  MEDIA RESPONSE - CACHEABILITY
                The cacheability to use in media response headers.
                Possible values: NoCache, Private, Public, Server, ServerAndNoCache, ServerAndPrivate
                Default value: private
          -->
         <setting name="MediaResponse.Cacheability" value="private" />
        !--  MEDIA RESPONSE - CacheExtensions
                The cache extension(s) to use in media response headers.
                Default value: ""
          -->
         <setting name="MediaResponse.CacheExtensions" value="" />
        <!--  MEDIA RESPONSE - MAX AGE
                The max age to use in media response headers.
                Set it to "00:00:00" to omit this header.
                Default value: 7.00:00:00 (seven days)
          -->
         <setting name="MediaResponse.MaxAge" value="7.00:00:00" />
         <!--  MEDIA RESPONSE - SLIDING EXPIRATION
                The sliding expiration to use in media response headers.
                Set it to "" to omit this header. To include it, use "true" or "false".
                Default value: ""
          -->
         <setting name="MediaResponse.SlidingExpiration" value="" />


    Reference:

    https://sdn.sitecore.net/upload/sdn5/articles%202/media/media%20facilities/sitecore%20media%20facilities.pdf



    https://doc.sitecore.net/sitecore_experience_platform/content_authoring/media_items/using_media_items/upload_a_file_to_the_media_library

    Friday, July 15, 2016

    Chrome Postman interceptor Send XHR requests from postman.

    Postman Interceptor

    No need of fiddler anymore. It is handy and easy to intercept any web api or api call, xhr request . This is useful tool when you want to intercept any request that is fired from any website.

    Installed this powerful extension in chrome. Also ensure you have postman client installed aswell.

    Now switch on the interceptor and you can even filter the incoming request to the website.

    And you can check the history tab of postman to see the request with output and all request body with details such as form post, post and get requests. 

    Sometime the request payload is so high it is usually difficult to copy and it hangs. Chrome is memory hungry.Now after you intercept all request you can even save it as collection and can refer for future reference.


    • Filter your requests
    • See the results just beneath it
    • Or you can view results in postman client and save it as collection.




    Finally all you need these?






    Tuesday, July 12, 2016

    Asp.net MVC : The length of the string exceeds the value set on the maxJsonLength property ?

    Introduction:

    When you happen to see this exception, i m sure you'll get n number of solutions in stackoverflow and trust me you've to understand your problem very well. There are scenarios based on which each of this solution works and differs.

    Problem Statement: 
    Json Request body max length issue if in my case was sending large file in encoding format.
    we used html 5 filereader api. There could be better solution to handle this we would have used HttpPostedFileBase So on.
    But we had some custom implementation and some
    design challenges. And the story is i ended up with below exception.
    Days of research helped me come up with different
    solution.

    Exception:

    • System.ArgumentException: Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property.
    • Parameter name: input
    • at System.Web.Script.Serialization.JavaScriptSerializer.Deserialize(JavaScriptSerializer serializer, String input, Type type, Int32 depthLimit)
    • at System.Web.Mvc.JsonValueProviderFactory.GetDeserializedObject(ControllerContext controllerContext)
    • at System.Web.Mvc.JsonValueProviderFactory.GetValueProvider(ControllerContext controllerContext)
    • at Castle.Proxies.Invocations.ValueProviderFactory_GetValueProvider.InvokeMethodOnTarget()
    • at Castle.DynamicProxy.AbstractInvocation.Proceed()
    • at Glimpse.Core.Extensions.AlternateMethodContextExtensions.TryProceedWithTimer(IAlternateMethodContext context, TimerResult& timerResult)
    • at Glimpse.Core.Extensibility.AlternateMethod.NewImplementation(IAlternateMethodContext context)
    • at Castle.DynamicProxy.AbstractInvocation.Proceed()
    • at Castle.Proxies.ValueProviderFactoryProxy.GetValueProvider(ControllerContext controllerContext)
    • at System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(ControllerContext controllerContext)
    • at System.Web.Mvc.ControllerBase.get_ValueProvider()
    • at System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor)
    • at System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    • at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)


    Solution that didn't work for me:

    No web.config settings helped to resolve my issue.
    Don't know why


    Solution That helped.


    Approach - Solution 1:


    Created Custom Value provider
    as per below stackoverflow answers.


    http://stackoverflow.com/questions/9943727/jsonmaxlength-exception-on-deserializing-large-json-objects

    The only problem is this woul applied to all controller and all action. Which is kind of security threat as per microsoft they explicetly apply maxJsonlength restriction. So again I want a solution which is very specific to Action of the controller.

    Approach - Solution 2: 

    Create custom Model binder or Action Filter specific to controller. Due to architecture of our solution this was causing some problem.

    In ASP.NET MVC, deserialize JSON prior to or in controller's action method

    http://stackoverflow.com/questions/1474896/in-asp-net-mvc-deserialize-json-prior-to-or-in-controllers-action-method


    The above solution was not compatible with solution architecture. It is also a compromise of custom validation that we had on model.


    Approach 3 : Solution [Cooked Up- Worked For me]


    Phill Haack blog caught my eye. It enlightened me to  some sort of brute force solution which in a way suits the overall design setup. This is win win situation.


    Sending JSON to an ASP.NET MVC Action Method Argument

    http://haacked.com/archive/2010/04/15/sending-json-to-an-asp-net-mvc-action-method-argument.aspx/

    This liner was breakthrough- Valueprovider vs ModelBinder 
    Jonathan had the unique insight to write a custom value provider which received JSON data and serialized it to a dictionary rather than the target object. The beauty of his approach is that this dictionary data is then passed to the default model binder which binds it to the final object with validation!

    
    

    What worked?

    
    

    I added custom deserialize json object with Maxvalue in override initialize method of MVC Controller.

    
    
    protected override void Initialize(RequestContext requestContext)
            {
                if ("Some specific Action")
                {
                    requestContext.HttpContext.Request.InputStream.Position = 0;
                    StreamReader reader = new StreamReader(requestContext.HttpContext.Request.InputStream);
                    string bodyText = reader.ReadToEnd();
                    if (String.IsNullOrEmpty(bodyText))
                    {
                        // no JSON data               
                    }
                    JavaScriptSerializer serializer = new JavaScriptSerializer();
                    serializer.MaxJsonLength = 2147483647;
                    object model = serializer.DeserializeObject(bodyText);
                    Dictionary < string, object > backingStore = new  Dictionary < string, object >(StringComparer.OrdinalIgnoreCase);
                    AddToBackingStore(backingStore, String.Empty, model);
                    ValueProvider = new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture);             
                }
                else
                {
                    base.Initialize(requestContext);
                }
            }
    

    I took AddToBackingStore method implementation from below post.

    http://stackoverflow.com/questions/9943727/jsonmaxlength-exception-on-deserializing-large-json-objects


    After few struggle my api was working fine but with few roadblock and hiccup ahead.



    I realized after fetching data from client side to my action controller i was hitting other api to post this data again. There is long design story , please don't ask me why.. The request was timing out.



    I have to add this.. mind it..this is just specific to this action and calling api stuff only not impacting other module. This is stop gap arrangement.


    I added this..to overcome httpclient timeout,

     Client.HttpClient.Timeout = new TimeSpan(1, 1, 1);

           // Serialize Request
                string _requestContent = null;
                _requestContent = SafeJsonConvert.SerializeObject(request, this.Client.SerializationSettings);
                _httpRequest.Content = new StringContent(_requestContent, Encoding.UTF8);
                _httpRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json; charset=utf-8");
                // Send Request
                if (_shouldTrace)
                {
                    ServiceClientTracing.SendRequest(_invocationId, _httpRequest);
                }
                cancellationToken.ThrowIfCancellationRequested();
                Client.HttpClient.Timeout = new TimeSpan(1, 1, 1);
                _httpResponse = await this.Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false);
    

    
    

    Some Configuration: Revisited.

    IIS Web server setting


    system.webServer
      security
        requestFiltering
          requestLimits maxAllowedContentLength="6291456" /
        /requestFiltering
      /security
    /system.webServer

    Asp.net System web

    httpRuntime maxRequestLength="51200" executionTimeout="3600" enableKernelOutputCache="false" relaxedUrlToFileSystemMapping="true" enableVersionHeader="false" 

    Server Script timeout

    http://stackoverflow.com/questions/579523/how-do-i-set-the-request-timeout-for-one-controller-action-in-an-asp-net-mvc-app

    HttpContext.Server.ScriptTimeout = 300; Or location path="ControllerName/ActionName" httpRuntime executionTimeout="1000"/ /system.web /location


    jsonSerialization maxJsonLength

    http://stackoverflow.com/questions/5692836/maxjsonlength-exception-in-asp-net-mvc-during-javascriptserializer
    This is more applicable when the JsonResult is huge


    system.web.extensions
      scripting
        webServices>
          jsonSerialization maxJsonLength="2147483644"/
        /webServices
      
    /system.web.extensions
    This is more applicable when the Json Request is huge . This didn't work most of the cases. So not fruitful solution whereas this globally applies to all controller action. Security threat.


    appSettings
      add key="aspnet:MaxJsonDeserializerMembers" value="150000" /

    /appSettings


    Few more story to hear. If you have patience to go through.Its worth reading.

    http://odetocode.com/blogs/scott/archive/2009/04/27/6-tips-for-asp-net-mvc-model-binding.aspx


    Search History:

    Why MaxJson was not set to heavenly large value? DOS Attack

    https://support.microsoft.com/en-in/kb/2661403

    https://support.microsoft.com/en-us/kb/981884

    https://social.msdn.microsoft.com/Forums/en-US/c017433c-0872-4689-a558-f0c2f301da10/javascriptserializer-deserialize-large-dictionary?forum=netfxnetcom


    What Stackoverflow to offer?

    Model Binder - To Deserialize the Json Object

    http://stackoverflow.com/questions/13746472/using-modelbinder-attribute-vs-modelbinders-add

    Custom Model binder derived from DefaulModelBinder

    http://stackoverflow.com/questions/25577747/modelbinder-deserialize-arbitrary-json

    In ASP.NET MVC, deserialize JSON prior to or in controller's action method

    http://stackoverflow.com/questions/1474896/in-asp-net-mvc-deserialize-json-prior-to-or-in-controllers-action-method

    Custom Value Provider 
    http://stackoverflow.com/questions/9943727/jsonmaxlength-exception-on-deserializing-large-json-objects



     

    Saturday, July 9, 2016

    Avoid querystring in Sitecore MVC using Route Redirect Approach

    Use Case : Keep Url clean without any querystring parameter. No additional encoding of url string or tampering by user.




    Note: This is not something related to Post Redirect Get Pattern in MVC
    http://www.asp.net/mvc/overview/security/preventing-open-redirection-attacks
    https://en.wikipedia.org/wiki/Post/Redirect/Get


    The sitecore web page contains a list of product and click on product item redirect to product detail page without querystring identifier.


    Solution:


    Idea is to set session at the interim between href event called and destination item page.


    In sitecore there will be controller rendering which is loaded as per sitecore item path url. In this controller we can have navigate method without view actionresult instead we return redirect as url. This navigate action is not control by sitecore item in fact is independent controller action which can be called outside sitecore controller rendering.





    public partial class ProductController : RenderingBaseController
    {
    
      public virtual ActionResult Navigate(int id)
            {
                _session.Set("Product", id);
                 return Redirect("/product/Detail");
            }
    }
    
    

    Route Registration in sitecore solution

    public partial class ProductController : RenderingBaseController
    {
    
      public class MyAreaRegistration : AreaRegistration 
        {
            public override string AreaName 
            {
                get 
                {
                    return "MyArea";
                }
            }
    
            public override void RegisterArea(AreaRegistrationContext context) 
            {
                context.MapRoute(
                    "myArea_default",
                    "myArea/{controller}/{action}/{id}",
                    new { action = "Index", id = UrlParameter.Optional },
                    new { controller = new RegexRouteConstraint("[^api]")  },
                    new string[] { "Sitecore.MVC.Areas.MyArea.Controllers" }
                );
            }
        }
    }
    }

    Wednesday, July 6, 2016

    Sitecore Powershell Extension Snippet- Create Custom User Defined Template Item



    As I said everything in Sitecore is indeed an Item. If you understand the way Item is represented knowing sitecore as CMS will be more easy.

    Use Case: I want to cut down my time to create user defined template by 50% rather than creating it from sitecore content editor or using sitecore rocks. I used Sitecore Powershell Extension to my rescue.

    Download https://marketplace.sitecore.net/en/Modules/Sitecore_PowerShell_console.aspx

    In my case, there are 4 steps involved while creating a user defined template.They are as follows:-


    • Create New Item using Template Item
    • Create Standard Value Item for this new item
    • Add Base Template which is already defined.
    • Add Common Renderings across template.

    The above is very specific to scenario I had in my project. Somehow I wanted to play with SPE. I tried this and it helped me save 50% of the work effort.

    Solution
    Go to TDS pick up any item and you will see the internal representation of field or standard field variables with key and name. Use this to set values using SPE.

    Important to remember , when you want to assign or set standard field values switch on RAW values option from Tab Menu->View-> click on checkbox to enable it,

    Using these raw values we can assign the predefined base template and renderings.

    You can checkout the code snippet in Gist for more details.

    Code Snippet 1






    Sample TDS Code : Lookup Standard Field Variables
    ----item----
    version: 1
    id: {F80C735D-CB69D16F9C4}
    database: master
    path: /sitecore/content/v/extrassage
    parent: {F90C17F3-5CB-85961DC46E1F}
    name: extras-usage
    master: {00000000-0000-0000-0000-000000000000}
    template: {5AD9859D54E9F61485B}
    templatekey: Menu Item

    ----field----
    field: {BA3F86A2779C1B5E}
    name: __Sortorder
    key: __sortorder
    content-length: 3

    500
    ----version----
    language: en
    version: 1
    revision: c9a8172a-0d3ea5665ed1

    ----field----
    field: {FF1A9AB0-84B35A2676}
    name: Title
    key: title
    content-length: 19

    Hope this is helpful.

    Sunday, June 12, 2016

    Run Powershell script from Gulp.js

    Installation Requirement

    1. If your Visual Studio is 2013 or order install Task Runner Explorer - Download and install- https://visualstudiogallery.msdn.microsoft.com/8e1b4368-4afb-467a-bc13-9650572db708
    2. Node.js- Recommended for most users-Check this download link-https://nodejs.org/en/
     
    • Run from the cmd command from this solution or class library location.
    • Ensure you keep the gulp.js in location where the actions /events are required.
    • Run command below from Class Library or website folder: Npm I gulp -g

    Change Environment variable path

    Gulp .js can be run from Task Runner VS2015

    var gulp = require("gulp");
    var childProcess = require('child_process');
    
    gulp.task('deploy-static-files', function (callback) {
        console.log("---------Start--------------------------");
        return childProcess.exec('Powershell.exe  -executionpolicy remotesigned -File  DeployStaticFiles.ps1', function (err, stdout, stderr) {
            console.log(stdout);
            console.log(stderr);
            if (err) console.log(err);
    
            console.log("Finished");
        });
    })
    

    Powershell script :Deploy Static file code

    ####----Copy files from source to destination-----
    $publishPathDestination      =   "Some Path"
    $scriptpathSource            = Split-Path $MyInvocation.MyCommand.Path
    Copy-Item  "$scriptpath"     $publishPathDestination   -Recurse -Force;
    
    

    Get description Attributes from Enum.

    using System;
    using System.ComponentModel;
    using System.Linq;
    using System.Reflection;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
             //Get Description
                Console.WriteLine(GetEnumDescription<MyEnum>(2));
    
                //Get Value
                Console.WriteLine(MyEnum.Hear);
    
                //Get Name
                Console.WriteLine(Enum.GetName(typeof(MyEnum), 1));
    
                //Get Enum Descriptions values
                var myEnumDescriptions =
                    Enum.GetValues(typeof(MyEnum))
                        .Cast<MyEnum>()
                        .Select(n => new { ID = (int)n, Name = Enum.GetName(typeof(MyEnum), n), Description = GetEnumDescription(n) });
    
                //Get Name, Description, Value
                foreach (var item in myEnumDescriptions)
                {
                    Console.WriteLine(item.Name);
                }
    
                //Iterate and fetch Description
                foreach (int iValues in Enum.GetValues(typeof(MyEnum)))
                {
                    Console.WriteLine(GetEnumDescription <MyEnum>(iValues));
                }
            }
    
            public enum MyEnum
            {
                [Description("You can Walk")]
                Walk = 1,
                [Description("You can Talk")]
                Talk = 2,
                [Description("You can Hear")]
                Hear = 3
            }
    
            public static string GetEnumDescription(Enum value)
            {
                FieldInfo fi = value.GetType().GetField(value.ToString());
    
                DescriptionAttribute[] attributes =
                    (DescriptionAttribute[])fi.GetCustomAttributes(
                        typeof(DescriptionAttribute),
                        false);
    
                if (attributes.Length > 0)
                    return attributes[0].Description;
                else
                    return value.ToString();
            }
    
            public static string GetEnumDescription <T>(int value)
            {
                return GetEnumDescription((Enum)(object)((T)(object)value));
            }
        }
    
    
    }