Tuesday, October 13, 2015

CRM ISV Module Creation


Are you planning to create any ISV module which is able to connect to CRM?


How can you refactor the code of  that module in such a way taht, it can connect to other CRM's as well- like siebel crm, oracle crm, custom build crm.

This post may help you for that. Here is the starting point. You can build further on top of this.:

Let us have base abstract class called BusinessEntity. 

This base class has an array of  attributes which can store key value pair.

SetAttributeValue and GetAttributeValue will help you to store and retrieve properties.
object this[string attributeName] will help you to pass attribute collection on the instance object.





public abstract class BusinesEntity
    {
     
        public AttributeCollection _attributes;

      
        public bool Contains(string attributeName)
        {
            return this.Attributes.Contains(attributeName);

        }
       
        public object this[string attributeName]
        {
            get
            {
                return this.Attributes[attributeName];
            }
            set
            {
                this.Attributes[attributeName] = value;
            }
        }
        public AttributeCollection Attributes
        {
            get
            {
                if (this._attributes == null)
                {
                    this._attributes = new AttributeCollection();
                }
                return this._attributes;
            }
            set
            {
                this._attributes = value;
            }
        }

        public virtual T GetAttributeValue<T>(string attributeLogicalName)
        {
            object attributeValue = this.GetAttributeValue(attributeLogicalName);
            if (attributeValue == null)
            {
                return default(T);
            }
            return (T)attributeValue;
        }
        private object GetAttributeValue(string attributeLogicalName)
        {
            if (string.IsNullOrWhiteSpace(attributeLogicalName))
            {
                throw new ArgumentNullException("attributeLogicalName");
            }
            if (!this.Contains(attributeLogicalName))
            {
                return null;
            }
            return this[attributeLogicalName];
        }
        protected virtual void SetAttributeValue(string attributeLogicalName, object value)
        {
            if (string.IsNullOrWhiteSpace(attributeLogicalName))
            {
                throw new ArgumentNullException("attributeLogicalName");
            }
            this[attributeLogicalName] = value;
        }
       
       
    }


public class AttributeCollection : DataCollection<string,object>
    {

    }

public abstract class DataCollection<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable
    {
        private IDictionary<TKey, TValue> _innerDictionary;
        private bool _isReadOnly;

        protected internal DataCollection()
        {
            this._innerDictionary = new Dictionary<TKey, TValue>();
        }

        public void Add(KeyValuePair<TKey, TValue> item)
        {
            this.CheckIsReadOnly();
            this._innerDictionary.Add(item);
        }

        public void Add(TKey key, TValue value)
        {
            this.CheckIsReadOnly();
            this._innerDictionary.Add(key, value);
        }

        public void AddRange(IEnumerable<KeyValuePair<TKey, TValue>> items)
        {
            if (items != null)
            {
                this.CheckIsReadOnly();
                ICollection<KeyValuePair<TKey, TValue>> is2 = this._innerDictionary;
                foreach (KeyValuePair<TKey, TValue> pair in items)
                {
                    is2.Add(pair);
                }
            }
        }

        public void AddRange(params KeyValuePair<TKey, TValue>[] items)
        {
            this.CheckIsReadOnly();
            this.AddRange((IEnumerable<KeyValuePair<TKey, TValue>>)items);
        }

        private void CheckIsReadOnly()
        {
            if (this.IsReadOnly)
            {
                throw new InvalidOperationException("The collection is read-only.");
            }
        }

        public void Clear()
        {
            this.CheckIsReadOnly();
            this._innerDictionary.Clear();
        }

        internal void ClearInternal()
        {
            this._innerDictionary.Clear();
        }

        public bool Contains(KeyValuePair<TKey, TValue> key)
        {
            return this._innerDictionary.Contains(key);
        }

        public bool Contains(TKey key)
        {
            return this._innerDictionary.ContainsKey(key);
        }

        public bool ContainsKey(TKey key)
        {
            return this._innerDictionary.ContainsKey(key);
        }

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            this._innerDictionary.CopyTo(array, arrayIndex);
        }

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return this._innerDictionary.GetEnumerator();
        }



        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            this.CheckIsReadOnly();
            return this._innerDictionary.Remove(item);
        }

        public bool Remove(TKey key)
        {
            this.CheckIsReadOnly();
            return this._innerDictionary.Remove(key);
        }

        internal bool RemoveInternal(TKey key)
        {
            return this._innerDictionary.Remove(key);
        }

        internal void SetItemInternal(TKey key, TValue value)
        {
            this._innerDictionary[key] = value;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this._innerDictionary.GetEnumerator();
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            return this._innerDictionary.TryGetValue(key, out value);
        }

        public int Count
        {
            get
            {
                return this._innerDictionary.Count;
            }
        }

        public virtual bool IsReadOnly
        {
            get
            {
                return this._isReadOnly;
            }
            internal set
            {
                this._isReadOnly = value;
            }
        }

        public virtual TValue this[TKey key]
        {
            get
            {
                return this._innerDictionary[key];
            }
            set
            {
                this.CheckIsReadOnly();
                this._innerDictionary[key] = value;
            }
        }

        public ICollection<TKey> Keys
        {
            get
            {
                return this._innerDictionary.Keys;
            }
        }

        public ICollection<TValue> Values
        {
            get
            {
                return this._innerDictionary.Values;
            }
        }
    }

Now Let us consume this abstract class in on sample class called Master record.
For each attribute you can set and get values by using GetAttributeValue and SetAttributeValue methods.
  public class MasterRecord: BusinesEntity
    {

        public virtual Guid Id
        {
            get { return this.GetAttributeValue<Guid>("id"); }
            set { this.SetAttributeValue("id", value); }
        }

        public virtual string SchemaName
        {
            get { return this.GetAttributeValue<string>("schemaName"); }
            set { this.SetAttributeValue("schemaName", value); }
        }
        public virtual string Content
        {
            get { return this.GetAttributeValue<string>("content"); }
            set { this.SetAttributeValue("content", value); }
        }
        public virtual bool IsDefault
        {
            get { return this.GetAttributeValue<bool>("isdefault"); }
            set { this.SetAttributeValue("isdefault", value); }
        }

        public virtual status Status
        {
            get { return this.GetAttributeValue<status>("status"); }
            set { this.SetAttributeValue("status", value); }
        }
        public virtual TypeOfPreferance TypeOfPreferance
        {
            get { return this.GetAttributeValue<TypeOfPreferance>("typeofpreferance"); }
            set { this.SetAttributeValue("typeofpreferance", value); }
        }

        public virtual bool IsOptIn
        {
            get { return this.GetAttributeValue<bool>("isoptin"); }
            set { this.SetAttributeValue("isoptin", value); }
        }
      
       
    }
You are done with basic structure CRM entity type implementation.

Now let us try to use this Master record class.
You can use it

          MasterRecord mObj = new MasterRecord();
           mObj["Id"] = new Guid();

            //You can pass attributes to the collection in two ways

            //Late Bind style
           mObj["SchemaName"] = "TestSchemaName";
           mObj["Status"] = 1;

            //Early bind style
           mObj.IsDefault = true;
           mObj.IsOptIn = false;

Look at the object. You can see a replica of CRM entity type




Like MasterRecord, you can have list of business entities as per your module. These business entities can map with equivalent entities of any CRM. Using factory pattern for Data Provider, you can connect to any type of CRM. I will post it in future. Stay Tune!
  

Happy Coding!