Thursday, January 31, 2019

Javascript design patterns

SharePoint Provider Hosted Add-ins 401 unauthorized error

We were getting an error 'Unauthorized Access' without any apparent reason. This error was resolved by following steps:


  1. Uninstall the app from SharePoint host website and re-install it again.

  1. Manually run SharePoint Central Admin timer job named 'Refresh Trusted Security Token Services Metadata feed'

Wednesday, January 30, 2019

Attribute caching in C#

Mentioning cache policy using an attribute at each model classes make it a flexible and easy to use approach.

In entity framework this can be implemented using GenericRepository and custom attributes.

So first, lets create an enum to represent the cache policies to be used in our application:


public enum WA1CachePoliciesEnum
    {
        None,
        SlidingExpiration10Minutes,
        AbsoluteExpiration1Hour,
        AbsoluteExpiration1Day,
        NeverExpire
    }

    Now we need to define the attribute



[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class CachePolicyAttribute : Attribute
    {
        public WA1CachePoliciesEnum policy;
        public CachePolicyAttribute(WA1CachePoliciesEnum policy)
        {
            this.policy = policy;
        }
    }
 The atrribute class merely saves CachedItemPolicy object passed to it in a public property. This attribute can be added to any class, but we intend to use it for our model classes only. Lets use the attribute in a model.

[CachePolicy(WA1CachePoliciesEnum.AbsoluteExpiration1Hour)]
    public class Students
    {
        [Key]
        public int StudentId { get; set; }

        public string Name { get; set; }

        [ForeignKey("Class")]
        [Column("ClassId")]
        public int ClassId { get; set; }
        public virtual Class Class { get; set; }


    }


Now we need to modify the Generic repository class to use the attribute

public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
    {
        internal CustomContext context;
        internal DbSet<TEntity> dbSet;
        internal DbContext dbcontext;
        protected ObjectCache cache = MemoryCache.Default;
        public GenericRepository(CustomContext context)
        {
            this.context = context;
            this.dbSet = context.Set<TEntity>();
        }

        public GenericRepository(DbContext dbcontext)
        {
            this.dbcontext = dbcontext;
            this.dbSet = context.Set<TEntity>();
        }

        public virtual IEnumerable<TEntity> Get()
        {
            Type entityType = typeof(TEntity);
            object[] policyAttributes = entityType.GetCustomAttributes(typeof(CustomCachePolicyAttribute), false);
            var result = new List<TEntity>();
            if (policyAttributes != null && policyAttributes.Length > 0)
            {
                var policy = CustomCacheHelper.GetPolicy(((CustomCachePolicyAttribute)policyAttributes[0]).policy);
                result = (List<TEntity>)cache.Get(entityType.Name);
                if (result == null)
                {
                    result = GetQuery().ToList();
                    cache.Add(new CacheItem(entityType.Name, result), policy);
                }
            }
            else
            {
                result = GetQuery().ToList();
            }

            return result;
        }

       

        public virtual IQueryable<TEntity> GetQuery()
        {
            IQueryable<TEntity> query = dbSet;
            return query;
        }

        public virtual IQueryable<TEntity> GetByIdAndIncludeRelated(object id, List<string> relatedEntities)
        {
            IQueryable<TEntity> query = dbSet.AsQueryable();
            if (relatedEntities != null)
            {
                query = relatedEntities.Aggregate(query,
                          (current, include) => current.Include(include));
            }
            // TEntity entity = dbSet.Find(id);
            // var entityKey = GetEntityKey(context, entity)
            // object res = entityKey.EntityKeyValues.FirstOrDefault().Value;
            //string key = entityKey.EntityKeyValues.FirstOrDefault().Key;
            return query;
        }

        public virtual IQueryable<TEntity> GetAndIncludeRelated(List<string> relatedEntities)
        {
            IQueryable<TEntity> query = dbSet.AsQueryable();
            if (relatedEntities != null)
            {
                query = relatedEntities.Aggregate(query,
                          (current, include) => current.Include(include));
            }
            return query;
        }

        public virtual EntityKey GetEntityKey<T>(DbContext context, T entity)
    where T : class
        {
            var oc = ((IObjectContextAdapter)context).ObjectContext;
            ObjectStateEntry ose;
            if (null != entity && oc.ObjectStateManager
                                    .TryGetObjectStateEntry(entity, out ose))
            {
                return ose.EntityKey;
            }
            return null;
        }

        public virtual EntityKey GetEntityKey<T>(DbContext context
                                               , DbEntityEntry<T> dbEntityEntry)
            where T : class
        {
            if (dbEntityEntry != null)
            {
                return GetEntityKey(context, dbEntityEntry.Entity);
            }
            return null;
        }

        public virtual TEntity GetByID(object id)
        {
            return dbSet.Find(id);
        }

        public virtual void Insert(TEntity entity)
        {
            dbSet.Add(entity);
        }

        public virtual void Delete(object id)
        {
            TEntity entityToDelete = dbSet.Find(id);
            Delete(entityToDelete);
        }

        public virtual void Delete(TEntity entityToDelete)
        {
            if (context.Entry(entityToDelete).State == EntityState.Detached)
            {
                dbSet.Attach(entityToDelete);
            }
            dbSet.Remove(entityToDelete);
        }

        public virtual void Update(TEntity entityToUpdate)
        {
            dbSet.Attach(entityToUpdate);
            context.Entry(entityToUpdate).State = EntityState.Modified;
        }

    }
In the code above, I have highlighted the Get() method which uses our custom cache attribute values. Every time we call get method on an entity, it checks if cache is enabled and if the entity is present in the cache before fetching data from database.


public class CustomCacheHelper
    {
        public static CacheItemPolicy GetPolicy(CustomCachePoliciesEnum cmCachePolicy)
        {
            var result = new CacheItemPolicy { AbsoluteExpiration = DateTime.MinValue };
            switch (cmCachePolicy)
            {
                case CustomCachePoliciesEnum.None:
                    break;
                case CustomCachePoliciesEnum.SlidingExpiration10Minutes:
                    result = new CacheItemPolicy { SlidingExpiration = new TimeSpan(0, 10, 0) };
                    break;
                case CustomCachePoliciesEnum.AbsoluteExpiration1Hour:
                    result = new CacheItemPolicy { AbsoluteExpiration = DateTime.Now.AddHours(1) };
                    break;
                case CustomCachePoliciesEnum.AbsoluteExpiration1Day:
                    result = new CacheItemPolicy { AbsoluteExpiration = DateTime.Now.AddDays(1) };
                    break;
                case CustomCachePoliciesEnum.NeverExpire:
                    result = new CacheItemPolicy { AbsoluteExpiration = DateTime.MaxValue };
                    break;
                default:
                    break;
            }
            return result;
        }
    }



This helper class creates cache item policies based on settings passed to it. 

Second level cache in entity framework

Here is a nuget package for second level cache in Entity Framework 6.1 or newer

https://github.com/moozzyk/EFCache

Thursday, January 24, 2019

Debug angular apps with vscode

To debug an angular app with vs code follow these steps:

  1. Start the application using npm start command
  2. Add a break point in the code
  3. Select the menu option Debug -> Start Debugging.

Friday, January 18, 2019

SharePoint provider hosted app: Error occurred

While trying to work with a provider hosted app, we sometimes get the error:

An error occurred while processing your request

There are several possible reasons for this error,  see Chris O'brian's blog for more details:

https://www.sharepointnutsandbolts.com/2015/07/errors-in-sharepoint-apps-add-ins.html.

He has mentioned some important files on SharePoint server like 
AppRegNew.aspx - Used to register a new provider hosted app
AppInv.aspx - Assign permissions, or view details of an already registered app
AppPrincipals.aspx - View or delete app principles registered on the SharePoint site

In my project I did the following changes to solve this error.


  1. Change app web site url from http to https in Project Properties window


2. In solution properties set Startup Project to 'Multiple startup projects' and action to 'Start' for both add in project and app web project


3. In app web project property pages set start action to 'Wait for a request from external application'


4. Make sure AppManifest.xml has {StandardTokens} appended to app web url


5. If a secondary client secret has been specified, make sure length of its value is divisible by four. '=' can be used as padding character.


Browse dot net framework source code

Microsoft has launched a browsable version of the .Net reference source code at https://referencesource.microsoft.com/.

Also, there is an extension available at Visual Studio marketplace which lets you press F12 to open the source code for anything in the .Net framework in a browser
https://marketplace.visualstudio.com/items?itemName=SchabseSLaks.Ref12#overview
https://github.com/SLaks/Ref12


c# httpclient The remote certificate is invalid according to the validation procedure: RemoteCertificateNameMismatch

 If we get this error while trying to get http reponse using HttpClient object, it could mean that certificate validation fails for the remo...