Tuesday, December 6, 2016

Using ExpandoObject with Dictionary to get rid of magic string

In most of the project we used the dictionary and along with dictionary there comes magic string.

In order to get rid of the magic string from code I utilize the ExpandoObject to add/update dictionary item by converting ExpandoObject into dynamic instance.

Usage

 
     Dictionary<string, string> dictoinary = new Dictionary<string, string>();
     dictoinary["FName"] = "Murtaza";
     dictoinary["LName"] = "Ali";
     dictoinary["DOB"] = "1/1/1001";

     var dynamicDictioinary = new DictionaryExpando<string>(dictoinary) as dynamic ;
     dynamicDictioinary.Gender = "Definitely Not Female";
     Console.WriteLine(dynamicDictioinary.FName);
     dynamicDictioinary.FName = "Murtaza Override";
     Console.WriteLine(dynamicDictioinary.FName);
     Console.WriteLine(dynamicDictioinary.Gender);

DictionaryExpando Class

 
    [Serializable]
    class DictionaryExpando<TValue> : DynamicObject
    {
        /// <summary>
        /// Instance of object passed in
        /// </summary>
        public Dictionary<string, TValue> Instance;

        /// <summary>
        /// Cached type of the instance
        /// </summary>
        private Type InstanceType;

        private PropertyInfo[] InstancePropertyInfo
        {
            get
            {
                if (_InstancePropertyInfo == null && Instance != null)
                    _InstancePropertyInfo =
                        Instance.GetType()
                                .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
                return _InstancePropertyInfo;
            }
        }

        private PropertyInfo[] _InstancePropertyInfo;


        public Dictionary<string,TValue> Properties = new Dictionary<string, TValue>();


        /// <summary>
        /// Allows passing in an existing instance variable to 'extend'.        
        /// </summary>
        /// <remarks>
        /// You can pass in null here if you don't want to 
        /// check native properties and only check the Dictionary!
        /// </remarks>
        /// <param name="instance"></param>
        public DictionaryExpando(Dictionary<string, TValue> instance)
        {
            Initialize(instance);
        }


        protected virtual void Initialize(Dictionary<string, TValue> instance)
        {
            Instance = instance;
            if (instance != null)
                InstanceType = instance.GetType();
        }


        /// <summary>
        /// Try to retrieve a member by name first from instance properties
        /// followed by the collection entries.
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            result = default(TValue);

            // first check the Properties collection for member
            if (Properties.Keys.Contains(binder.Name))
            {
                result = Properties[binder.Name];
                return true;
            }


            // Next check for Public properties via Reflection
            if (Instance != null)
            {
                try
                {
                    return GetProperty(Instance, binder.Name, out result);
                }
                catch
                {
                }
            }

            // failed to retrieve a property
            result = null;
            return false;
        }


        /// <summary>
        /// Property setter implementation tries to retrieve value from instance 
        /// first then into this object
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {

            // first check to see if there's a native property to set
            if (Instance != null)
            {
                try
                {
                    bool result = SetProperty(Instance, binder.Name, value);
                    if (result)
                        return true;
                }
                catch
                {
                }
            }

            // no match - set or add to dictionary
            Properties[binder.Name] = (TValue)value;
            return true;
        }

        /// <summary>
        /// Dynamic invocation method. Currently allows only for Reflection based
        /// operation (no ability to add methods dynamically).
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="args"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            if (Instance != null)
            {
                try
                {
                    // check instance passed in for methods to invoke
                    if (InvokeMethod(Instance, binder.Name, args, out result))
                        return true;
                }
                catch
                {
                }
            }

            result = null;
            return false;
        }


        /// <summary>
        /// Reflection Helper method to retrieve a property
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="name"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        protected bool GetProperty(object instance, string name, out object result)
        {
            if (instance == null)
                instance = this;
            result = default(TValue);
            try
            {
                var dictonary = instance as Dictionary<string, TValue>;
                result = dictonary[name];
                return true;

            }
            catch (Exception)
            {
            }

            return false;
        }

        /// <summary>
        /// Reflection helper method to set a property value
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="name"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        protected bool SetProperty(object instance, string name, object value)
        {
            if (instance == null)
                instance = this;

            try
            {
                Dictionary<string, TValue> dictionary = instance as Dictionary<string, TValue>;
                dictionary[name] = (TValue)value;
                return true;
            }
            catch (Exception)
            {
            }

            return false;
        }

        /// <summary>
        /// Reflection helper method to invoke a method
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="name"></param>
        /// <param name="args"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        protected bool InvokeMethod(object instance, string name, object[] args, out object result)
        {
            if (instance == null)
                instance = this;

            // Look at the instanceType
            var miArray = InstanceType.GetMember(name,
                                                 BindingFlags.InvokeMethod |
                                                 BindingFlags.Public | BindingFlags.Instance);

            if (miArray != null && miArray.Length > 0)
            {
                var mi = miArray[0] as MethodInfo;
                result = mi.Invoke(Instance, args);
                return true;
            }

            result = null;
            return false;
        }



        /// <summary>
        /// Convenience method that provides a string Indexer 
        /// to the Properties collection AND the strongly typed
        /// properties of the object by name.
        /// 
        /// // dynamic
        /// exp["Address"] = "112 nowhere lane"; 
        /// // strong
        /// var name = exp["StronglyTypedProperty"] as string; 
        /// </summary>
        /// <remarks>
        /// The getter checks the Properties dictionary first
        /// then looks in PropertyInfo for properties.
        /// The setter checks the instance properties before
        /// checking the Properties dictionary.
        /// </remarks>
        /// <param name="key"></param>
        /// 
        /// <returns></returns>
        public object this[string key]
        {
            get
            {
                try
                {
                    // try to get from properties collection first
                    return Properties[key];
                }
                catch (KeyNotFoundException ex)
                {
                    // try reflection on instanceType
                    object result = null;
                    if (GetProperty(Instance, key, out result))
                        return result;

                    // nope doesn't exist
                    throw;
                }
            }
            set
            {
                if (Properties.ContainsKey(key))
                {
                    Properties[key] = (TValue)value;
                    return;
                }

                // check instance for existance of type first
                var miArray = InstanceType.GetMember(key, BindingFlags.Public | BindingFlags.GetProperty);
                if (miArray != null && miArray.Length > 0)
                    SetProperty(Instance, key, value);
                else
                    Properties[key] = (TValue)value;
            }
        }


        /// <summary>
        /// Returns and the properties of 
        /// </summary>
        /// <param name="includeProperties"></param>
        /// <returns></returns>
        public IEnumerable<KeyValuePair<string, object>> GetProperties(bool includeInstanceProperties = false)
        {
            if (includeInstanceProperties && Instance != null)
            {
                foreach (var prop in this.InstancePropertyInfo)
                    yield return new KeyValuePair<string, object>(prop.Name, prop.GetValue(Instance, null));
            }

            foreach (var key in this.Properties.Keys)
                yield return new KeyValuePair<string, object>(key, this.Properties[key]);

        }


        /// <summary>
        /// Checks whether a property exists in the Property collection
        /// or as a property on the instance
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool Contains(KeyValuePair<string, object> item, bool includeInstanceProperties = false)
        {
            bool res = Properties.ContainsKey(item.Key);
            if (res)
                return true;

            if (includeInstanceProperties && Instance != null)
            {
                foreach (var prop in this.InstancePropertyInfo)
                {
                    if (prop.Name == item.Key)
                        return true;
                }
            }

            return false;
        }

    }



Instance

The Isntance property will return the real dictionary object to perform additional operations if required. You may need convert dynamic object back into ExpandoObject

 public Dictionary<string, TValue> Instance;
  

TryGetMember and GetProperty

TryGetMember is overridden method of ExpandoObject in order to fetch the value of provided propertyname. The dynamic property name can be retrieved from binder.Name.

GetProperty fetches the value from dictionary for provided key.

 
     public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            result = default(TValue);

            // first check the Properties collection for member
            if (Properties.Keys.Contains(binder.Name))
            {
                result = Properties[binder.Name];
                return true;
            }


            // Next check for Public properties via Reflection
            if (Instance != null)
            {
                try
                {
                    return GetProperty(Instance, binder.Name, out result);
                }
                catch
                {
                }
            }

            // failed to retrieve a property
            result = null;
            return false;
        }

  protected bool GetProperty(object instance, string name, out object result)
        {
            if (instance == null)
                instance = this;
            result = default(TValue);
            try
            {
                var dictonary = instance as Dictionary<string, TValue>;
                result = dictonary[name];
                return true;

            }
            catch (Exception)
            {
            }

            return false;
        }


TrySetMember and SetProperty

TrySetMember is overridden method of ExpandoObject in order to set the value of provided propertyname. The dynamic property name can be retrieved from binder.Name.

SetProperty sets the value to dictionary for provided key

 
 public override bool TrySetMember(SetMemberBinder binder, object value)
        {

            // first check to see if there's a native property to set
            if (Instance != null)
            {
                try
                {
                    bool result = SetProperty(Instance, binder.Name, value);
                    if (result)
                        return true;
                }
                catch
                {
                }
            }

            // no match - set or add to dictionary
            Properties[binder.Name] = (TValue)value;
            return true;
        }

      protected bool SetProperty(object instance, string name, object value)
        {
            if (instance == null)
                instance = this;

            try
            {
                Dictionary<string, TValue> dictionary = instance as Dictionary<string, TValue>;
                dictionary[name] = (TValue)value;
                return true;
            }
            catch (Exception)
            {
            }

            return false;
        }


Note: If you wan to use the remove function of dictionary then you may still need to use the magic string to remove the key from dictionary.

Saturday, December 3, 2016

Make Every Page In EPiServer As Draggable As Single Tile

In one of our EPiServer Project we wanted to make every page as draggable and wanted to make them appear as single tile. Therefore, I came up with approach, In this approach we will make every base page available to implement ISingleTileView interface and we would register one template coordinator for given Interface.

ISingleTileView


 public interface ISingleTileView
    {
        string Title { get; }
        string TileDescription { get; }
        string ImageUrl { get; }
        string LinkToUrl { get; }
    }


We had 5 different base classes for CMS and ECommerce. All of them are listed below.

  1. SitePageData
  2. SiteBlockData
  3. BaseVariantContent
  4. BaseProductContent
  5. BaseCategory
The Class definitions of above all classes are defined as

SitePageData

 
 public abstract class SitePageData : PageData, ISingleTileView
    {
        public string Title { get; }
        public string TileDescription { get; }
        public string ImageUrl { get; }
        public string LinkToUrl { get; }
    }


SiteBlockData

 
 public abstract  SiteBlockData : BlockData, ISingleTileView
    {
        public string Title { get; }
        public string TileDescription { get; }
        public string ImageUrl { get; }
        public string LinkToUrl { get; }
    }


BaseVariantContent

 
public class BaseVariantcContent : VariationContent, ISingleTileView
    {
        public string Title { get; }
        public string TileDescription { get; }
        public string ImageUrl { get; }
        public string LinkToUrl { get; }
    } 

BaseProductContent

 
public class BaseProductContent : ProductContent, ISingleTileView
    {
        public string Title { get; }
        public string TileDescription { get; }
        public string ImageUrl { get; }
        public string LinkToUrl { get; }
    }


BaseCategory

 
   public class BaseCategory : NodeContent, ISingleTileView
    {
        public string Title { get; }
        public string TileDescription { get; }
        public string ImageUrl { get; }
        public string LinkToUrl { get; }
    }

You can populate above properties with what ever other properties.

We have used below CMS properties to return value of ISingleTileView properties

Assigning Value To ISingleTileView Properties

 
        #region ISingle Tile View

        [Ignore]
        [ScaffoldColumn(false)]
        public string Title
        {
            get { return Heading; }
        }

        [Ignore]
        [ScaffoldColumn(false)]
        public string TileDescription
        {
            get { return Abstract.ToHtmlString(); }
        }

        [Ignore]
        [ScaffoldColumn(false)]
        public string ImageUrl
        {
            get { return this.GetDefaultAsset(AssetMediaNames.THUMBNAIL).GetFriendlyUrl(); }
        }

        [Ignore]
        [ScaffoldColumn(false)]
        public string LinkToUrl
        {
            get { return ContentLink.GetFriendlyUrl(); }
        }

        #endregion

TemplateCoordinator

 
  [ServiceConfiguration(typeof(IViewTemplateModelRegistrator))]
    public class TemplateCoordinator : IViewTemplateModelRegistrator
    {
        public const string BlockFolder = "~/Views/Shared/Blocks/";
        public const string PagePartialsFolder = "~/Views/Shared/PagePartials/";

        public void Register(TemplateModelCollection viewTemplateModelRegistrator)
        {
            // All Pages will be Single Tile View will rendered below Partial view
            viewTemplateModelRegistrator.Add(typeof(ISingleTileView), new TemplateModel()
            {
                Name = "Single Tile View For Any Page",
                Inherit = true,
                Tags = new[] { Constants.TemplateDescriptorTags.SingleTile },
                Path = BlockPath("Single.Tile.cshtml"),
                TemplateTypeCategory = TemplateTypeCategories.MvcPartialView,
                AvailableWithoutTag = true,
                Default = false
            });

        public static string BlockPath(string fileName)
        {
            return string.Format("{0}{1}", BlockFolder, fileName);
        }

        public static string PagePartialPath(string fileName)
        {
            return string.Format("{0}{1}", PagePartialsFolder, fileName);
        }
    }


SingleTile Partial View

In the following path we have our sinlgetileview partial view ~/Shared/Blocks/Single.Tile.cshtml

 
@using EPiServer.Reference.Commerce.Site.Core.Settings
@using EPiServer.Reference.Commerce.Site.Infrastructure.Extensions
@model EPiServer.Reference.Commerce.Site.Core.ContentTypes.TemplateCoordinatorInterface.ISingleTileView
<li>
    <div class="product-item">
<div class="product-item-image" style="background-image: url(@Html.ResizeImageUrl(Model.ImageUrl, preset: AssetMediaNames.ImageResizerPresets.PRODUCT_RANGE_TILE));">
<img alt="@Model.Title" src="@Html.ResizeImageUrl(Model.ImageUrl, preset: AssetMediaNames.ImageResizerPresets.PRODUCT_RANGE_TILE)" />
        </div>
<h2>
@Model.Title</h2>
<div>
@Html.Raw(Model.TileDescription)
        </div>
<a class="btn btn-default" href="@Model.LinkToUrl">More info</a>
    </div>
</li>


Entity Framework Code First Custom Database Initialization and Check if Table Already Exists

In one of our Project we had to use existing database and we wanted to use the code first entity framework as well.

There was requirement to make sure that some of the tables are already existed and all the new ones will be created using the code first approach.

Therefore, I decided to customized the MigrateDatabaseToLatestVersion.

First we will verify that the prerequisite tables are already there and then based on the auto update app setting we will migrate database to latest version.

GiftVoucherDatabaseInitialization 


public class GiftVoucherDatabaseInitialization : MigrateDatabaseToLatestVersion<GiftVoucherEntityTypesConfigurationDbContext, Migrations.Configuration>
    {
        private string IsCodeFirstAutoMigrationEnabled = "IsCodeFirstAutoMigrationEnabled";

        public GiftVoucherDatabaseInitialization(string connectionStringName) : base(connectionStringName)
        {
        }

        public override void InitializeDatabase(GiftVoucherEntityTypesConfigurationDbContext context)
        {
            var queryString = @"
                         SELECT 1 FROM sys.tables AS T
                         INNER JOIN sys.schemas AS S ON T.schema_id = S.schema_id
                         WHERE S.Name = 'dbo' AND T.Name = '{0}'";
            if (context.Database.Exists())
            {
                bool exists = context.Database
                      .SqlQuery<int?>(string.Format(queryString, "tbl_PurchaseVoucher"))
                      .SingleOrDefault() != null;

                if(!exists)
                    throw new ContentNotFoundException("Business Foundataion Entity{PurchaseVoucher} Not Found");

                exists = context.Database
                      .SqlQuery<int?>(string.Format(queryString, "tbl_PurchaseVoucherRedemption"))
                      .SingleOrDefault() != null;

                if (!exists)
                    throw new ContentNotFoundException("Business Foundation Entity{PurchaseVoucherRedemption} Not Found");
            }

            if (IsMigrationEnabled())
            {
                base.InitializeDatabase(context);
            }
        }

        private bool IsMigrationEnabled()
        {
            var migration = ConfigurationManager.AppSettings[IsCodeFirstAutoMigrationEnabled];
            return migration != null && migration.ToLower().Equals("true");
        }
    }

In Global.asax file, we will set the database initializer for above context

Setting Up Database Initializer


        protected void Application_Start()
        {
            // Add the database migration strategy to update the database on app start
            Database.SetInitializer(new GiftVoucherDatabaseInitialization(ConnectionStringConfig.CommerceConnectionStringName));

            // Run the initializer, but don't force
            new GiftVoucherEntityTypesConfigurationDbContext().Database.Initialize(true);
        }


GiftVoucherEntityTypesConfigurationDbContext

The GiftVoucherEntityTypesConfigurationDbContext looks like below



public class GiftVoucherEntityTypesConfigurationDbContext : DbContext
    {
        private readonly ICustomerContextFacade _customerContextFacade;

        public GiftVoucherEntityTypesConfigurationDbContext()
      : this("EcfSqlConnection", null)
     {
      
     }

        public GiftVoucherEntityTypesConfigurationDbContext(string nameOrConnectionString)
            : base(nameOrConnectionString)
        {
            
        }

        public DbSet<PurchaseVoucher> PurchaseVouchers { get; set; }
  public DbSet<PurchaseVoucherRedemption> PurchaseVoucherRedemptions { get; set; }

  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
   // Dynamically load all configuration
   var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
                .Where(type => !string.IsNullOrEmpty(type.Namespace))
                .Where(type => type.BaseType != null && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
            foreach (var type in typesToRegister)
            {
                dynamic configurationInstance = Activator.CreateInstance(type);
                modelBuilder.Configurations.Add(configurationInstance);
            }
            
            base.OnModelCreating(modelBuilder);
        }
     }

Monday, November 28, 2016

Adding New MetaDataPlus Dictionary Field and Assigning it to Catalog Entry Class

We will be adding new catalog entry class and will be assigning new meta dictionary field with multiple values.

Adding new Catalog Entry (ProductBase) Class



    [CatalogContentType(GUID = "C39413F3-310C-49C6-A4C4-6B86D6788401", MetaClassName = "ProductBase")]
    public class ProductBase  : ProductContent
    {
       [Searchable]
        [IncludeInDefaultSearch]
        [BackingType(typeof(PropertyDictionarySingle))]
        [Display(Order = 60)]
        [Required]
        public virtual string ProductType { get; set; }
    }



Now we will be defining initializing class to add new meta field and assign it to Catalog Entry(ProductBase) class



[ModuleDependency(typeof(ServiceContainerInitialization))]
 [InitializableModule]
 public class CatalogInitialization : IConfigurableModule
 {
  private bool _isInitialized;

  public void Initialize(InitializationEngine context)
  {
   if (_isInitialized)
    return;

   SyncMetaClassAndField();

   _isInitialized = true;
  }

  public void Uninitialize(InitializationEngine context)
  {
   _isInitialized = false;
  }

  public void ConfigureContainer(ServiceConfigurationContext context)
  {
   context.Container.Configure(ctx => ctx.AddRegistry());
  }

  private static void SyncMetaClassAndField()
  {
    var metaProClass = Mediachase.MetaDataPlus.Configurator.MetaClass.Load(MetaDataContext.Instance, "ProductBase");
                  MetaField metaProp = MetaField.Load(MetaDataContext.Instance, "ProductType");

      if (metaProp == null)
      {
                      metaProp = Mediachase.MetaDataPlus.Configurator.MetaField.Create(MetaDataContext.Instance, "Mediachase.Commerce.Catalog", "ProductType", "Product Type", "Product Type Description", MetaDataType.DictionarySingleValue, 1000, false, false, false, false);
                    }
                  metaProp.Dictionary.Add("Baby", "Baby");
                  metaProp.Dictionary.Add("Toddler", "Toddler");
                  metaProp.Dictionary.Add("Junior", "Junior");
                  metaProp.Dictionary.Add("Walker", "Walker");
                  metaProClass.AddField(metaProp);
        }
 }

Thursday, November 3, 2016

Using EPiServer IMigratoinSteps To Automatically Add EPi Commerce Market, Warehouse, Payment and Shipping Method

In one of my application i had to setup Initial Market, Mulitple Warehouses, Payment Method and Shipping Method in order to make thing available for editors.

I got some of the help from the QuickSilver ImportSiteContent Class. However, it was missing how to add new market and warehouses.

I will strongly recommend to look at above class to understand more about it.

We will now go through how to add Market, Warehouses, Shipping Method and Payment Method.

IMigrationStep

In one of my question that i asked on EPiServer Forum, the guy replied that IMigrationStep is an internal class and should not be used because it will void the support contract and can have break change impact in future release. So please guys use it with caution.

We will need to decorate our class

[ServiceConfiguration(typeof(IMigrationStep))]



[ServiceConfiguration(typeof(IMigrationStep))]
    public class ImportNZSiteContent : IMigrationStep
    {   
 public int Order { get { return 2010; } }
        public string Name { get { return "Adding Market, Warehouse and Shipping"; } }
        public string Description { get { return "Adding NZ/AU Market, Multiple Warehouses for NZ and Free Shipping method"; } }
  
 public bool Execute(IProgressMessenger processMessenger)
        {
            _progressMessenger =  processMessenger;
            _progressMessenger.AddProgressMessageText("Adding/Updating New Zealand Market and Warehouse", false, 0);
            try
            {
   
     }   
            catch (Exception exception)
            {
                _processMessenger.AddProgressMessageText("ImportTreasureSiteContent failed: " + exception.Message + "Stack trace:" + exception.StackTrace, true, 0);                
                return false;
            }
            return true;
        }
     }

Re-Testing 

The Migration will only run once per database therefore, if you want to re-test then change the name or namespace of the class and re run the site.

Verify Migration Step
Once you will brows the CMS site it and login with Admin credential, System will provide you the migration step summary. 

Another way to check if migration was run successfully is to look at the view [dbo].[VW_EPiServer.Commerce.Internal.Migration.MigrationStepInfo]) in CMS Site.



Market

By Default EPi Commerce provides IMarket Implementaion MarketImpl but this class does not have setter function to set new values therefore, we will need to add our own class for IMarket interface. We will inherit from the MarketImp in order keep thing simple and easy.


internal class SiteMarketImp : MarketImpl, IMarket
    {
        public new MarketId MarketId { get; set; }
        public new bool IsEnabled { get; set; }
        public new string MarketName { get; set; }
        public new string MarketDescription { get; set; }
        public new CultureInfo DefaultLanguage { get; set; }
        public new IEnumerable<CultureInfo> Languages { get; set; }
        public new Currency DefaultCurrency { get; set; }
        public new IEnumerable<Currency> Currencies { get; set; }
        public new IEnumerable<string> Countries { get; set; }

        /// <summary>
        /// Creates an empty IMarket instance with the specified market identifier.
        /// </summary>
        /// <param name="marketId"></param>
        public SiteMarketImp (MarketId marketId) : base(marketId)
        {
            this.MarketId = marketId;
        }

        /// <summary>Creates a copy of the passed IMarket instance.</summary>
        /// <param name="market">The IMarket instance to copy.</param>
        public SiteMarketImp (IMarket market) : base(market)
        {
            this.MarketId = market.MarketId;
            this.IsEnabled = market.IsEnabled;
            this.MarketName = market.MarketName;
            this.MarketDescription = market.MarketDescription;
            this.DefaultLanguage = market.DefaultLanguage;
            this.DefaultCurrency = market.DefaultCurrency;
        }
    }


Function to Add/Update Market


    private IMarket AddNzMarket()
        {
            MarketId NZ_Market_ID = new MarketId("NZL"); 
            var nzCulture = CultureInfo.GetCultureInfo("en-NZ");
            var nzCurrency = new Currency("NZD");
            var allMarkets = _marketService.GetAllMarkets();
            var nzMarket = allMarkets.FirstOrDefault(m => m.MarketId == NZ_Market_ID);
            bool isCreating = nzMarket == null;
            var updatedNzMarket = isCreating ? new SiteMarketImp (new MarketId("NZL")) : new SiteMarketImp (nzMarket);

            /* Get the country just to make sure it exists */
            var countryDto = CountryManager.GetCountry("NZL", false);

            /* countryDto Table 0 is for State/Province and 1 is for Country 
             * countryDto Row is to get the country code
             */
            if (countryDto == null || countryDto.Tables.Count < 2 || countryDto.Tables[1].Rows.Count < 0)
                throw new ContentNotFoundException("New Zealand Country Could not be found in EPi Commerce");

            /* All the requried fields need to be populated */
            updatedNzMarket.MarketName = "New Zealand";
            updatedNzMarket.MarketDescription = "New Zealand";
            updatedNzMarket.Countries = new List<string> { "NZL" };
            updatedNzMarket.Currencies = new List<Currency> { nzCurrency };
            updatedNzMarket.DefaultCurrency = nzCurrency;
            updatedNzMarket.Languages = new List<CultureInfo> { nzCulture };
            updatedNzMarket.DefaultLanguage = nzCulture;
            updatedNzMarket.IsEnabled = true;

           /* Save NZ Market */
            if (isCreating)
                _marketService.CreateMarket(updatedNzMarket);
            else
                _marketService.UpdateMarket(updatedNzMarket);

            return updatedNzMarket;
        }


Add/Update Warehouses



    private void AddNzWarehouses()
        {
            /* Adding/Updating two ware houses */
            var nzSouth = "NZSouth";
            var nzNorth = "NZNorth";
            var warehouses =_warehouseRepository.List().ToList();

            /* Add/Update South Island Warehouse */
            var nzWarehouse = warehouses.FirstOrDefault(w => w.Code == nzSouth);
            var nzSouthWarehouse= nzWarehouse == null ? new Warehouse() : new Warehouse(nzWarehouse);
            SaveWarehouse(nzSouthWarehouse, nzSouth, "New Zealand South Island Warehouse", true);

            /* Add/Update North Island Warehouse */
            nzWarehouse = warehouses.FirstOrDefault(w => w.Code == nzNorth);
            var nzNorthWarehouse = nzWarehouse == null ? new Warehouse() : new Warehouse(nzWarehouse);
            SaveWarehouse(nzNorthWarehouse, nzNorth, "New Zealand North Island Warehouse", false);
        }   
     private void SaveWarehouse(Warehouse warehouse, string code, string name, bool isPrimary)
        {
            warehouse.ApplicationId = AppContext.Current.ApplicationId;
            warehouse.Code = code;
            warehouse.Name = name;
            warehouse.IsActive = true;
            warehouse.IsPrimary = isPrimary;
            warehouse.IsFulfillmentCenter = true;
            warehouse.IsPickupLocation = false;
            warehouse.IsDeliveryLocation = true;
            warehouse.ContactInformation = new WarehouseContactInformation(warehouse.ContactInformation);
            _warehouseRepository.Save(warehouse);
        }


Add/Update ShippingMethod



  private void AddNzShippingMethod(IMarket market)
        {
            var markets = new List<IMarket> {market};
            foreach (var language in market.Languages.Distinct())
            {
                var languageId = language.TwoLetterISOLanguageName;
                var dto = ShippingManager.GetShippingMethods(languageId);
                DeleteShippingMethods(dto);
                ShippingManager.SaveShipping(dto);

                var shippingSet = CreateShippingMethodsForLanguageAndCurrencies(dto, markets, languageId);
                ShippingManager.SaveShipping(dto);

                AssociateShippingMethodWithMarkets(dto, markets, shippingSet);
                ShippingManager.SaveShipping(dto);
            }
        }

        private void DeleteShippingMethods(ShippingMethodDto dto)
        {
            foreach (var method in dto.ShippingMethod)
            {
                method.Delete();
            }
        }

        private IEnumerable<ShippingMethodDto.ShippingMethodRow> CreateShippingMethodsForLanguageAndCurrencies(ShippingMethodDto dto, IEnumerable<IMarket> markets, string languageId)
        {
            var shippingOption = dto.ShippingOption.First(x => x.Name == "Generic Gateway");
            var shippingMethods = new List<ShippingMethodDto.ShippingMethodRow>();
            var sortOrder = 1;

            var nzdCostRegular = new Money(0, Currency.NZD);

            foreach (var currency in markets.SelectMany(m => m.Currencies).Distinct())
            {
                shippingMethods.Add(CreateShippingMethod(dto, shippingOption, languageId, sortOrder, "Regular-" + currency, string.Format("Regular {0} (4 - 5 days) ({1})", currency, languageId), nzdCostRegular, currency));
            }

            return shippingMethods;
        }

        private ShippingMethodDto.ShippingMethodRow CreateShippingMethod(ShippingMethodDto dto, ShippingMethodDto.ShippingOptionRow shippingOption, string languageId, int sortOrder, string name, string description, Money costInNzd, Currency currency)
        {
            return dto.ShippingMethod.AddShippingMethodRow(
                Guid.NewGuid(),
                shippingOption,
                AppContext.Current.ApplicationId,
                languageId,
                true,
                name,
                "Free Delivery",
                costInNzd.Amount,
                currency,
                description,
                false,
                sortOrder,
                DateTime.Now,
                DateTime.Now);
        }

        private void AssociateShippingMethodWithMarkets(ShippingMethodDto dto, IEnumerable<IMarket> markets, IEnumerable<ShippingMethodDto.ShippingMethodRow> shippingSet)
        {
            foreach (var shippingMethod in shippingSet)
            {
                foreach (var market in markets.Where(m => m.Currencies.Contains<Currency>(shippingMethod.Currency)))
                {
                    dto.MarketShippingMethods.AddMarketShippingMethodsRow(market.MarketId.Value, shippingMethod);
                }
            }
        }

Thursday, October 27, 2016

Pre and Post Action/Function Decorator

In one of the old project, I had to intercept the call for the existing function in order to log and do some stuff therefore, In order to achieve that without modifying the existing function as it was working and was old enough not to touch. Therefore, After going through resources i came across this article which describes the function decorator.

I decided to implement the ActionExtension to implement Pre and Post Interceptor for existing functions.

The ActionExtension function looks like below.

Note: The action interceptor are need to be defined depending on the number of parameters. I had to implement it for action which accepted only two parameters therefore i had two action extension method which accepts two parameters. For different number of parameters you will need to add more action extensions.



 public static class ActionExtension
    {
        public static Action<targ1, targ2> PreProcessor<targ1, targ2>(this Action<targ1, targ2> action, Action<targ1, targ2> preActionToRun, Targ1 tArg1, Targ2 tarArg2)
        {
            return (TRArg1, TRarg2) =>
            {
                /* PreProcessing */
                preActionToRun(tArg1, tarArg2);

                /* Processing */
                action(tArg1, tarArg2);
            };
        }

        public static Action<targ1, targ2> PostProcessor<targ1, targ2>(this Action<targ1, targ2> action, Action<targ1, targ2> postActionToRun, Targ1 tArg1, Targ2 tarArg2)
        {
            return (TRArg1, TRarg2) =>
            {

                /* Processing */
                action(tArg1, tarArg2);

                /* PostProcessing */
                postActionToRun(tArg1, tarArg2);
            };

        }
    }

Wednesday, October 19, 2016

Converting Object Specific Properties to IDictionary<string, Object> Using Lambda Expression

Below function will convert the specific properties of any class to IDictionary<string, object>

The function is  an extension method for object type and takes params array of Expression<Func<T, object>>. The reason for outputting object in Func is to output any primary type such int, long, string, float and etc.

Registration Example Class



public class Registration 
{
        public int RegId {get; set;}
        public string RegName {get; set;}
        public string RegNotRequired {get; set;}
        public DateTime RegDate {get; set;}
        public string RegLocation {get; set;}
}


Example



Registration reg = new Registration 
                            {RegId = 1,
                             RegName = "Murtaza", 
                             RegNotRequired="Not Important", 
                             RegDate="2012-09-19", 
                             RegLocation ="Pakistan"};
IDictionary<string, object> val = reg.AsKeyAndValue(
                                      r => r.RegId, r => r.RegName, 
                                      r => r.RegDate, r => r.RegLocation);




Extension Method


Following function converts the class properties into IDictionary<string, object>


  public static IDictionary AsObjectValueDictionary(this T obj,
            params Expression>[] source)
        {
            Dictionary objectValyeDictionary = new Dictionary();
            foreach (var src in source)
            {
                var keyValue = obj.AsKeyValuePair(src);
                if(!objectValyeDictionary.ContainsKey(keyValue.Key))
                    objectValyeDictionary.Add(keyValue.Key, keyValue.Value);
            }

            return objectValyeDictionary;
        }


Helper Fucntion For Property Name


The below helper method is use to extract the property name from lambda expression


        public static KeyValuePair AsKeyValuePair(this object obj, Expression> propertyLambda)
        {
            var me = propertyLambda.Body as MemberExpression;
            UnaryExpression ue;
            if (me == null)
            {
                ue = propertyLambda.Body as UnaryExpression;

                if(ue == null)
                    throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");

                me = (MemberExpression) ue.Operand;
            }

            string memberName = me.Member.Name;
            var value = obj.GetType().GetProperty(memberName).GetValue(obj, null);
            return new KeyValuePair(me.Member.Name, value);
        }

Monday, October 17, 2016

Microsoft Bot Builder .Net Using FormFlow: Bot With Dependency Injection - Part 5

In this part

  1. We will include IoC Container using AutoFac for our service within Bot Form.

Lets Start

First we will have sample service which will provide product detail. This function is just prototype we won't provide any implementation.

Interface


    public interface IPimsServiceClient
    {
        IList GetModelDetail(int ProductId);

        /* Other functions as per requirements */
    }


Implementation



    public class PimsServiceClient : IPimsServiceClient
    {
        public IList GetModelDetail(int ProductId)
        {
            /*Implementation*/
        }

        /* Other functions as per requirements */
    }


Global.asax.cs

Update Global.asax.cs to setup IoC container using Autofac.


     public class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            var builder = new ContainerBuilder();

            // Get your HttpConfiguration.
            var config = GlobalConfiguration.Configuration;

            // Register your Web API controllers.
            builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();

            // OPTIONAL: Register the Autofac filter provider.
            builder.RegisterWebApiFilterProvider(config);

            builder.RegisterType().As().WithParameter("serviceUrl", ConfigurationManager.AppSettings["PimsServiceUrl"]);

            builder.RegisterType().PropertiesAutowired();

            // Set the dependency resolver to be Autofac.
            var container = builder.Build();
            config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

            GlobalConfiguration.Configure(WebApiConfig.Register);
        }
    }

Updating MessageController

We will add IComponentContext to message controller to resolve any services.



 public class MessagesController : ApiController
    {
        public static IComponentContext Context { get; set; }

        public MessagesController(IComponentContext context)
        {
           Context = context;
        }

        internal static IDialog MakeRootDialog()
        {
            return Chain.From(() => new FormDialog(new RegistrationForm(), Context.Resolve<RegistrationFormBuilder>().BuildForm));
        }
....
....


Adding Service as static property inside RegistrationFormBuilder


 [Serializable]
    public class RegistrationFormBuilder
    {
        public static IPimsServiceClient PimsServiceClient { get; set; }
        .....
        .....
        .....
        
         return new FormBuilder()
                    .Message("Welcome to the product registration bot!")
                    .Field(nameof(RegistrationForm.FirstName))
                    .Field(nameof(RegistrationForm.LastName))
                    .Field(nameof(RegistrationForm.Email))
                    .Field(nameof(RegistrationForm.ModelNumber), validate: async (state, value) =>
                {
                     var modelsSuggestion =  PimsServiceClient.GetModelDetail(productId);
                     ....
                     ....


This property can be use anywhere within RegistrationFormBuilder.


Tuesday, October 4, 2016

Microsoft Bot Builder .Net Using FormFlow: Real World Example of Product Registration - Part 4

In this part

  1. We will modify our Bot to work with real world example.
  2. We will create Bot that will ask use series of questions required to register product.
  3. We will also use validation to validate user input.

Lets Start

First we will have Registration Form in which we will have following fields required for registering any product.

  1. FirstName (string)
  2. LastName (string)
  3. Email (string, email)
  4. ModelNumber (string)
  5. DateOfPurchase (DateTime)
  6. State (Enum, {NSW, VIC, TAS, QLD, WA, NT})
  7. KeepMeUpdatedForProductsAndPromotions (bool)
Lets start by defining the classes

Registration Model


 [Serializable]
    public class RegistrationForm
    {
        public string FirstName { get; set; }

        public string LastName { get; set; }

        [Pattern(@"^\w+([\.+_])*\w+@\w+(\.\w+)+$")]
        [Template(TemplateUsage.NotUnderstood, "Email \"{0}\" is not valid.")]
        public string Email { get; set; }

        public string ModelNumber { get; set; }

        public DateTime DateOfPurchase { get; set; }

        [Prompt("At what {&} did you purchase product? {||}", ChoiceFormat = "{1}")]
        public List State { get; set; }
        [Describe("Whould you like to be keep updated regarding new products and promotions?")]
        public bool KeepMeUpdatedForProductsAndPromotions { get; set; }
       
    }

This is very simple model class to hold registration data.
This model defines basic properties together with these properties

  • We have used Pattern Attribute to validate email address through Regex.
  • We have used state enum to provide list to select from
  • We will use custom validation for Model number to validate the model number



Registration Form Builder Class


 [Serializable]
    public class RegistrationFormBuilder
    {

        public IForm BuildForm()
        {
            System.Net.ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
 
             OnCompletionAsyncDelegate  processOrder = async (context, state) =>
            {
                var message = context.MakeMessage();
                message.Text = "Your registration has been successfull.";
                await context.PostAsync(message);
            };

     
            return new FormBuilder()
                    .Message("Welcome to the product registration bot!")
                    .Field(nameof(RegistrationForm.FirstName))
                    .Field(nameof(RegistrationForm.LastName))
                    .Field(nameof(RegistrationForm.Email))
                    .Field(nameof(RegistrationForm.ModelNumber), validate: async (state, value) =>
                    {
                       // Validate Model number here
                        ValidateResult validateResult = new ValidateResult()
                        {
                            /*if modelSuggestion != null && modelsSuggestion.Any()*/
                            IsValid = true, //Set true or false
                            Value = value
                        };

                        return validateResult;
                    })
                    .Field(nameof(RegistrationForm.DateOfPurchase))
                    .Field(nameof(State))
                    .Field(nameof(RegistrationForm.KeepMeUpdatedForProductsAndPromotions))
                    .Confirm("Are the information correct?" +
                             "\n\rFirst Name:{FirstName}" +
                             "\n\rLast Name:{LastName}" +
                             "\n\rEmail:{Email}" +
                             "\n\r{&ModelNumber}:{ModelNumber}" +
                             "\n\r{&DateOfPurchase}:{DateOfPurchase}" +
                             "\n\rState:{State}" +
                             "\n\r{&KeepMeUpdatedForProductsAndPromotions}:{KeepMeUpdatedForProductsAndPromotions}?")
                    .OnCompletion(processOrder)
                    .Build();
        }
    }


The above Registration Form Builder is very simple. It will enable bot to ask one by one each property. Furthermore, the Bot is intelligent enough to ask for user input based on variable types.

How to Call Registration Form Builder in MessageController


 internal static IDialog MakeRootDialog()
        {
            var pimServiceClient = Context.Resolve();
            return Chain.From(() => new FormDialog(new RegistrationForm(), Context.Resolve().BuildForm));
        }

        [ResponseType(typeof(void))]
        public virtual async Task Post([FromBody] Activity activity)
        {
            if (activity != null)
            {
                
                // one of these will have an interface and process it
                switch (activity.GetActivityType())
                {
                    case ActivityTypes.Message:
                        await Conversation.SendAsync(activity, MakeRootDialog);
                        break;
                    case ActivityTypes.ConversationUpdate:
                    case ActivityTypes.ContactRelationUpdate:
                    case ActivityTypes.Typing:
                    case ActivityTypes.DeleteUserData:
                    default:
                        Trace.TraceError($"Unknown activity type ignored: {activity.GetActivityType()}");
                        break;
                }
            }
            var response = Request.CreateResponse(HttpStatusCode.OK);
            return response;
         }


Once you compile the solution and run the Emulator you should see the below flow of conversation.




In the next part we will enable the dependency injection of our service using Autofac.

Thursday, September 29, 2016

Unit testing Fake IP Address for Website

We were trying to fake the IP Address for one of our website for different visitors. Such as if some one visits the site from Australia or New Zealand then site should redirect to au or Beefeaterbbq.co.nz respectively.
We were able to test the redirect for Australia quite easily as we were based in Australia but for New Zealand it was bit of hard work as we could not find any Proxies that could fake NZ IP address for us. After doing searching and bit of struggle i have figured out we can alter Fiddler script to fake request header.
Therefore, I was able to add one line to output IP Address of my choice to whatever site as long as Fiddler intercepts the call from browser. Below are the steps to add custom header and test the site.

Pre-requisite

  1. Fiddler 2
  2. Any Browser :>

Steps to Fake IP Addresses

  • Open Fiddler and goto Rules
  • Select Customize Rules - This will open the CustomRules.js file in notepad


  • Search for function OnBeforeRequest

static function OnBeforeRequest(oSession: Session) {


    • At the end of function add below line

    oSession.oRequest.headers.Add("X-Forwarded-For", "14.1.63.255");  // NZ IP Address, DNS name or IP address of target server


    • Save the file
    • Make sure on fiddler -> File (Menu) -> Capture Traffic is checked

    • Once you are done don't forget to remove your line from CustomRules.js

    Wednesday, September 28, 2016

    Microsoft Bot Builder .Net Using FormFlow - Part 3

    In this part we will test our live azure service on Microsoft Bot Emulator Channel.

    Let first make sure our azure service is up and running.

    Live Bot Url Testing


    Make sure azure live url (http://botapplication120160926104506.azurewebsites.net/api/messages) on browsing returns result similar to below screen shot.












    Make sure live azure service returns success on bot page




    Connecting to Microsoft Bot Emulator Channel


    Lets launch emulator and enter the below fields

    • Bot Url
    • Microsoft App Id
    • Microsoft App Password


    Once you enter the above field and enter any message you will see something like below screen shot.
     


    The reason for failing of message is that live azure could not connect back to Emulator url.
    In order to make our bot url available to azure live url we need to work with NGROK.

    NGORK

    Ngor is small utility which makes local port number available to outside world.
    you can download the ngork from the following url (https://ngrok.com/download)

    Once downloaded the ngrok run the following command through command prompt
    ngrok http -host-header=rewrite 9001



    Once you run the command you will see the below output


    The forwarding url is the url availble to outside world, which is mapped to our local port 9001
    Copy that url and paste in Microsoft Emulator Url



    Now you type the message you will see the output 


    Things to remember


    • Bot url must have /api/messages appended
    • Emulator url can have http or https 
    • Bot url can have http or https
    In next part we will modify our bot to use real world example of registering product.

    Microsoft Bot Builder .Net Using FormFlow - Part 2

    In this part we will deploy the Bot Application that we have created in Part 1 to Azure Service and also register our bot to Bot Directoty in order to use Live Azure Service Url.

    Publishing Bot

    Firstly, we will publish the Bot to Azure. In order  to publish to Azure Services you will need to purchase azure services or If you have MSDN subscription for visual studio then you can have free monthly credit of $200 something.


    • Coming back to Publish, right click to project and select publish. 




















    • Then, click on Microsoft Azure App Service. 
    • Then, click on publish, you will have to with your azure login credentials. 
    • Once login you should be seeing below screen






    • Click New button this will open popup to create new App inside Azure Service. Fill the required fields and select OK




    • Once you have complete wizard will provide all the details of server, url, deployment credentials and etc. 

    We will need only live url for our Bot and everything else can be leave for now. Save the password so you don't have to remember it.


    • Click publish to publish the site. Once site is published successfully. you should be seeing below screen.


    Registering Bot


    In order to use live url we would need to register our Bot to https://dev.botframework.com/.
    Please follow the below steps to register your newly created Bot and generate MicrosoftAppId and MicrosoftAppPassword.


    • Go to the given url https://dev.botframework.com/bots/new
    • Login with Microsoft Credentials
    • Fill in the requried fields
    • Enter messaging endpoint: Live Azure url + /api/messages (http://botapplication120160926104506.azurewebsites.net/api/messages)
    • Click on Create Microsoft App Id and Password













    • Remember the App Id and Password that will be required to update the web.config and publish again to Azure.














    • Fill the remaining fields






    • After creating the bot you should see the below screen


    If you press the Test then you will see the Unauthorized message. This is because we have published the bot with empty Microsoft App Id and Password.
    Now we will get the Id and Password created during Bot registration and update the web.config and publish it again to Azure Service.

    Updating Microsoft AppId and MicrosoftAppPassword

    Now head back to visual studio and update the web.config below app settings

      <add key="BotId" value="DemoBot" />
       <add key="MicrosoftAppId" value="786f2e79-1642-4d2c-a051-21d4e034e826" />
       <add key="MicrosoftAppPassword" value="Mz2OO8HPjeNfkqWz9JjWGZp" />

    Now publish the Bot Application to Azure site. You will just need to select the already created Azure App instead of creating new App.

    Testing Live Bot Url


    If you go back to registered bot and now when you click on Test then you should see the Endpoint authorization succeeded.





    This tells that our service is up and running on Azure and Bot is registered as well.

    In next Part we will see how can configure Emulator to use Live Azure endpoint and test our bot.

    Sunday, September 25, 2016

    Microsoft Bot Builder .Net Using FormFlow - Part 1

    Microsoft Bot Builder is new and very powerful feature that can be use to utilise chat channels to automate communicate and do conversation with real world user.

    There are very good documentation how to start with Microsoft Bot Builder. Below are the links where you can quickly start with bot builder.

    How to install and setup project
    https://docs.botframework.com/en-us/csharp/builder/sdkreference/gettingstarted.html

    Getting started with Bot Builder
    https://docs.botframework.com/en-us/csharp/builder/sdkreference/

    Install and use Bot Emulator to test your Bot
    https://docs.botframework.com/en-us/tools/bot-framework-emulator/

    You will also find very good example on below link for understanding of basic FormFlow structure and usage.
    https://docs.botframework.com/en-us/csharp/builder/sdkreference/forms.html

    I will be providing the walk through of  real world example where user can register the purchased product with company. I will start from creating basic bot application to real world example by distributing articles in different parts.

    Usually for any type of registration below needs to be consider

    • In a form there will be fields that needs to be answer step by step.
    • Each fields need to be validated against certain requirement.
    • Submit the form at the end.
    • Send/Show confirmation message.


    If you have followed above links then you should be able to create new project using Visual Studio.

    Add New Project




    one thing to consider in web.config. There are three app settings

    
       <add key="BotId" value="YourBotId" />
    
       <add key="MicrosoftAppId" value="" />
    
       <add key="MicrosoftAppPassword" value="" />
    
    

    MicrofotAppId and MicrosoftAppPassword needs to be empty during development and debugging.
    We will have those settings when we will register our bot to https://dev.botframework.com/.
    We will be need to update this settings during the deployment of our app to Azure Service.


    Once you have project created you already have ready to use bot. Where user can send a message and bot will reply with number of characters in a message.

    If you want to test you can start project.

    Start Bot Application

    Debug (Menu) ---> Start Debugging. This will start web page with below message

    Start Bot Emulator


    Make sure Bot Base Url matches the url of your application and api/messages needs to appended to communicate with you message web api controller.


    Send/Receive Message through Emulator



    Once you received the message with number of characters then we are sured our Bot is now in working condition.

    This is the end of Part 1. In Part 2 we will be Registering our Bot to  https://dev.botframework.com/ and publishing our App to Azure Service.

    Friday, September 23, 2016

    Generic Comparer for DistinctBy Using Func and IComparer For IEnumerable

    During development of many projects, I have been come across to develop IComparer for different reasons and situations. Each time creating new comparer for different classes. Therefore, I have found solution to use single generic Comparer for all the IEnumerable extension which requires IComparer for the class.

    I have used DistinctBy Example to go throw the Generic Extension Method.


    
     public static class Compare
        {
            public static IEnumerable<T> DistinctBy<T, TIdentity>(this IEnumerable<T> source, Func<T, TIdentity> identitySelector)
            {
                return source.Distinct(Compare.By(identitySelector));
            }
    
            public static IEqualityComparer<TSource> By<TSource, TIdentity>(Func<TSource, TIdentity> identitySelector)
            {
                return new DelegateComparer<TSource, TIdentity>(identitySelector);
            }
    
            private class DelegateComparer<T, TIdentity> : IEqualityComparer<T>
            {
                private readonly Func<T, TIdentity> identitySelector;
    
                public DelegateComparer(Func<T, TIdentity> identitySelector)
                {
                    this.identitySelector = identitySelector;
                }
    
                public bool Equals(T x, T y)
                {
                    return Equals(identitySelector(x), identitySelector(y));
                }
    
                public int GetHashCode(T obj)
                {
                    return identitySelector(obj).GetHashCode();
                }
            }
        }

    DistinctBy Extension Method


    The function simply creates an extension method for IEnumerable together with Func with input of Type and outputting property of type TIdentity.

    Using this way any class can provide the property that needs to be used for comparing and calculating hashcode for that property. However, TIdentity usually will be the unique key for the class object.

    IEqualityComparer

    This function simply create and object of Comparer passing in the Func delegate. The resulting DelegateComparer will use the Func Delegate to fetch the unique property and compare the unique property and calculate the hashcode.

    Usage

    
    public class AClass
    {
      public string AProperty {get; set;}
    }
    
    public IEnumerable<AClass> DistinctAClass(IEnumerable<AClass> aClassList)
    {
        aClassList.DistinctBy(a => a.AProperty);
    }
    
    

    Hope this helps to create unnecessary classes for comparer.

    Tuesday, September 20, 2016

    Working with Live Database

    Working with Live Database

    Connecting to live database from development environment


    1 - Never connect to live database from developer environment
    2 - If it is necessary to debug live database then ask for the live back up to be restored on staging database server
    3 - If it is necessary to work directly to live database from developer environment then follow below precautions


    If connecting to live database from developer environment then following things need to be considered


    First and foremost ask to take backup of live database so we don't loose any data - no backup, no work on live database


    * If connecting through code

    • Make sure all the scheduler are commented out/turned off
    • Make sure all the external service push or pull is commented our/turned off
    • Never update live database from developer environment - Fix the code and push it to live server and re run it.

    * If connecting through Sql server from developer environment


    • Always wraps command in BEGIN TRAN command see example below and comment out the COMMIT TRAN section so nothing applies to live server mistakenly
    BEGIN TRAN 
     update aa set City='chennai',LastName='vinoth'; 
    -- if update is what you want then
    -- COMMIT TRAN 
    -- if NOT then
    ROLLBACK

    • It's VERY IMPORTANT to remember to either COMMIT or ROLLBACK; This wouldn't be a good time to go to lunch while forgetting the transaction open!  :-) Open Transaction would lock the database.
    • If Update is necessary then always ask  I.T guy to do backup
    • If it is small task then make sure you run the select command first to make sure it will affect only required table and row for .eg

    SELECT Column1 , Column2
    -- UPDATE t SET Column1 = x, Column2 = y
    FROM MyTable AS t
    WHERE ...



    Always do the peer review before running any script or code on live databsae