internal static object ToEntityField(PropertyInfo prop, SPListItem listItem, SPField field = null,
                                             object value = null, bool reloadLookupItem = true)
        {
            string propName = prop.Name;
            Type   propType = prop.PropertyType;

            //  var fieldAttrs = (FieldAttribute[])prop.GetCustomAttributes(typeof(FieldAttribute), true);
            var fieldAttrs = (FieldAttribute[])Attribute.GetCustomAttributes(prop, typeof(FieldAttribute), true);

            string spPropName;

            if (fieldAttrs.Length != 0)
            {
                spPropName = fieldAttrs[0].Name;
                if (spPropName == null)
                {
                    spPropName = propName;
                }
            }
            else
            {
                spPropName = FieldMapper.TranslateToFieldName(propName);
            }

            if (field == null)
            {
                field = listItem.Fields.TryGetFieldByStaticName(spPropName);
                if (field == null)
                {
                    throw new SharepointCommonException(string.Format("Field '{0}' not exist", propName));
                }
            }
            object fieldValue = value ?? listItem[spPropName];

            //------------------------------------

            if (field.Type == SPFieldType.User)
            {
                var f = field as SPFieldLookup;
                Assert.NotNull(f);
                if (f.AllowMultipleValues == false)
                {
                    var spUser = CommonHelper.GetUser(field.ParentList, spPropName, fieldValue);
                    if (spUser == null)//todo: why?
                    {
                        if (fieldValue == null)
                        {
                            return(null);
                        }
                        var userValue = new SPFieldUserValue(field.ParentList.ParentWeb, fieldValue.ToString());
                        return(_proxyGenerator.CreateClassProxy <User>(new UserAccessInterceptor(userValue)));
                    }
                    else
                    {
                        return(_proxyGenerator.CreateClassProxy <Person>(new UserAccessInterceptor(spUser)));
                    }
                }
                else
                {
                    var spUsers = CommonHelper.GetUsers(field.ParentList, spPropName, fieldValue);
                    var users   = new UserIterator(spUsers);
                    return(users);
                }
            }

            // lookup
            if ((field.Type == SPFieldType.Lookup || field.Type == SPFieldType.Invalid) && typeof(Item).IsAssignableFrom(propType))
            {
                var attr = fieldAttrs[0];
                return(GetLookupItemUntype(propType, field, fieldValue, attr));
            }

            //multi lookup
            if ((field.Type == SPFieldType.Lookup || field.Type == SPFieldType.Invalid) &&
                (CommonHelper.ImplementsOpenGenericInterface(propType, typeof(IEnumerable <>))))
            {
                var attr       = fieldAttrs[0];
                var lookupType = propType.GetGenericArguments()[0];

                if (!typeof(Item).IsAssignableFrom(lookupType))
                {
                    throw new SharepointCommonException(string.Format("Type {0} cannot be used as lookup", lookupType));
                }

                try
                {
                    var meth        = typeof(EntityMapper).GetMethod("GetLookupItems", BindingFlags.Static | BindingFlags.NonPublic);
                    var methGeneric = meth.MakeGenericMethod(lookupType);
                    var itmId       = listItem == null ? null : (int?)listItem.ID;
                    return(methGeneric.Invoke(null, new[] { field, fieldValue, attr, itmId, reloadLookupItem }));
                }
                catch (TargetInvocationException e)
                {
                    throw e.InnerException;
                }
            }

            if (field.Type == SPFieldType.Guid)
            {
                var guid = new Guid(fieldValue.ToString());
                return(guid);
            }

            if (field.Type == SPFieldType.Note)
            {
                if (fieldValue == null)
                {
                    return(null);
                }

                var text = ((SPFieldMultiLineText)field).GetFieldValueAsText(fieldValue);
                return(text);
            }

            if (propName == "Version")
            {
                var version = new Version(fieldValue.ToString());
                return(version);
            }

            if (field.Type == SPFieldType.Number)
            {
                if (propType == typeof(int))
                {
                    int val = Convert.ToInt32(fieldValue);
                    return(val);
                }

                if (propType == typeof(double))
                {
                    var val = Convert.ToDouble(fieldValue);
                    return(val);
                }

                if (CommonHelper.ImplementsOpenGenericInterface(propType, typeof(Nullable <>)))
                {
                    Type argumentType = propType.GetGenericArguments()[0];
                    if (argumentType == typeof(int))
                    {
                        return(fieldValue == null ? (int?)null : Convert.ToInt32(fieldValue));
                    }
                    if (argumentType == typeof(double))
                    {
                        return(fieldValue == null ? (double?)null : Convert.ToDouble(fieldValue));
                    }
                }

                if (typeof(Item).IsAssignableFrom(propType))
                {
                    if (fieldAttrs.Length == 0 || string.IsNullOrEmpty(fieldAttrs[0].LookupList))
                    {
                        throw new SharepointCommonException("To map number field as Item it need been marked with 'FieldAttribute' and set 'LookupList' property");
                    }
                    var attr = fieldAttrs[0];
                    return(GetLookupItemUntype(propType, field, fieldValue, attr));
                }
            }

            if (field.Type == SPFieldType.Boolean)
            {
                if (propType == typeof(bool))
                {
                    var val = Convert.ToBoolean(fieldValue);
                    return(val);
                }
                if (CommonHelper.ImplementsOpenGenericInterface(propType, typeof(Nullable <>)))
                {
                    var argumentType = propType.GetGenericArguments()[0];
                    if (argumentType == typeof(bool))
                    {
                        return(fieldValue == null ? (bool?)null : Convert.ToBoolean(fieldValue));
                    }
                }

                if (typeof(Item).IsAssignableFrom(propType))
                {
                    if (fieldAttrs.Length == 0 || string.IsNullOrEmpty(fieldAttrs[0].LookupList))
                    {
                        throw new SharepointCommonException(
                                  "To map number field as Item it need been marked with 'FieldAttribute' and set 'LookupList' property");
                    }
                    var attr = fieldAttrs[0];
                    return(GetLookupItemUntype(propType, field, fieldValue, attr));
                }
            }

            if (field.Type == SPFieldType.DateTime)
            {
                if (propType == typeof(DateTime))
                {
                    var val = CommonHelper.GetDateTimeFieldValue(fieldValue);
                    return(val);
                }
                if (CommonHelper.ImplementsOpenGenericInterface(propType, typeof(Nullable <>)))
                {
                    var argumentType = propType.GetGenericArguments()[0];
                    if (argumentType == typeof(DateTime))
                    {
                        var v = fieldValue == null ? (DateTime?)null : CommonHelper.GetDateTimeFieldValue(fieldValue);
                        return(v);
                    }
                }
            }

            if (field.Type == SPFieldType.Currency)
            {
                if (propType == typeof(decimal))
                {
                    decimal val = Convert.ToDecimal(fieldValue);
                    return(val);
                }

                if (CommonHelper.ImplementsOpenGenericInterface(propType, typeof(Nullable <>)))
                {
                    Type argumentType = propType.GetGenericArguments()[0];
                    if (argumentType == typeof(decimal))
                    {
                        return(fieldValue == null ? (decimal?)null : Convert.ToDecimal(fieldValue));
                    }
                }
            }

            if (field.Type == SPFieldType.Text)
            {
                if ((string)fieldValue == string.Empty)
                {
                    return(null);
                }
                return(fieldValue);
            }

            if (field.Type == SPFieldType.Choice)
            {
                if (propType.IsEnum == false)
                {
                    if (CommonHelper.ImplementsOpenGenericInterface(propType, typeof(Nullable <>)))
                    {
                        Type argumentType = propType.GetGenericArguments()[0];
                        if (argumentType.IsEnum == false)
                        {
                            throw new SharepointCommonException(string.Format("Property '{0}' must be declared as enum with fields corresponds to choices", propName));
                        }
                        propType = argumentType;
                    }

                    if (propType == typeof(string))
                    {
                        return(fieldValue);
                    }
                }

                return(EnumMapper.ToEntity(propType, fieldValue));
            }

            if (field.Type == SPFieldType.Calculated)
            {
                fieldValue = field.GetFieldValueAsText(fieldValue);
            }

            return(fieldValue);
        }
        internal static Field ToFieldType(MemberInfo member)
        {
            var propertyInfo = member as PropertyInfo;

            if (propertyInfo == null)
            {
                var methodInfo = member as MethodInfo;
                if (methodInfo == null)
                {
                    Assert.Inconsistent();
                }
                var    declaringType = methodInfo.DeclaringType;
                string trimGet       = member.Name.Substring(4);
                propertyInfo = declaringType.GetProperty(trimGet);
            }

            Type           propType        = propertyInfo.PropertyType;
            string         spName          = TranslateToFieldName(propertyInfo.Name);
            var            fieldAttrs      = propertyInfo.GetCustomAttributes(typeof(FieldAttribute), true);
            var            customAttrs     = (CustomPropertyAttribute[])propertyInfo.GetCustomAttributes(typeof(CustomPropertyAttribute), true);
            string         dispName        = null;
            bool           isMultilineText = false;
            object         defaultValue    = null;
            bool           required        = false;
            FieldAttribute attr            = null;

            if (fieldAttrs.Length != 0)
            {
                attr = (FieldAttribute)fieldAttrs[0];
                var spPropName = attr.Name;
                if (spPropName != null)
                {
                    spName = spPropName;
                }
                dispName        = attr.DisplayName;
                isMultilineText = attr.IsMultilineText;
                required        = attr.Required;
                defaultValue    = attr.DefaultValue;
            }


            var field = new Field {
                Name     = spName, Property = propertyInfo, DisplayName = dispName,
                Required = required, DefaultValue = defaultValue, FieldAttribute = attr,
            };

            if (customAttrs.Length != 0)
            {
                var typeAttr = customAttrs.FirstOrDefault(ca => ca.Name == "Type");
                if (typeAttr != null)
                {
                    field.Type = (SPFieldType)Enum.Parse(typeof(SPFieldType), typeAttr.Value);
                    return(field);
                }
            }

            if (attr != null && attr.FieldProvider != null)
            {
                field.Type = SPFieldType.Invalid;
                return(field);
            }

            if (propType == typeof(string))
            {
                field.Type = isMultilineText ? SPFieldType.Note : SPFieldType.Text;
                return(field);
            }

            if (isMultilineText)
            {
                throw new SharepointCommonException("[IsMultilineText] can be used only for text fields.");
            }

            if (propType == typeof(Version) || propType == typeof(Guid))
            {
                field.Type = SPFieldType.Text;
                return(field);
            }

            if (propType == typeof(DateTime) || propType == typeof(DateTime?))
            {
                field.Type = SPFieldType.DateTime;
                return(field);
            }


            if (propType == typeof(User))
            {
                field.Type = SPFieldType.User;
                return(field);
            }

            if (propType == typeof(double) || propType == typeof(double?) ||
                propType == typeof(int) || propType == typeof(int?))
            {
                field.Type = SPFieldType.Number;
                return(field);
            }

            if (propType == typeof(decimal) || propType == typeof(decimal?))
            {
                field.Type = SPFieldType.Currency;
                return(field);
            }

            if (propType == typeof(bool) || propType == typeof(bool?))
            {
                field.Type = SPFieldType.Boolean;
                return(field);
            }

            // lookup fields
            if (typeof(Item).IsAssignableFrom(propType))
            {
                // lookup single value
                if (attr == null)
                {
                    throw new SharepointCommonException("Lookups must be marked with [SharepointCommon.Attributes.FieldAttribute]");
                }

                field.Type        = SPFieldType.Lookup;
                field.LookupList  = attr.LookupList;
                field.LookupField = attr.LookupField ?? "Title";
                return(field);
            }

            if (CommonHelper.ImplementsOpenGenericInterface(propType, typeof(IEnumerable <>)))
            {
                Type argumentType = propType.GetGenericArguments()[0];

                //// user multi value

                if (argumentType == typeof(User))
                {
                    field.Type         = SPFieldType.User;
                    field.IsMultiValue = true;
                    return(field);
                    //return new Field {Type = SPFieldType.User, Name = propertyInfo.Name, IsMultiValue = true,};
                }

                //// lookup multi value

                if (attr == null)
                {
                    throw new SharepointCommonException("Lookups must be marked with [SharepointCommon.Attributes.FieldAttribute]");
                }


                if (typeof(Item).IsAssignableFrom(argumentType))
                {
                    field.Type         = SPFieldType.Lookup;
                    field.LookupList   = attr.LookupList;
                    field.LookupField  = attr.LookupField;
                    field.IsMultiValue = true;
                    return(field);
                }
            }

            if (propType.IsEnum)
            {
                field.Type    = SPFieldType.Choice;
                field.Choices = EnumMapper.GetEnumMemberTitles(propType);
                if (field.Choices.Any() == false)
                {
                    throw new SharepointCommonException("enum must have at least one field");
                }
                return(field);
            }

            if (CommonHelper.ImplementsOpenGenericInterface(propType, typeof(Nullable <>)))
            {
                Type argumentType = propType.GetGenericArguments()[0];

                if (argumentType.IsEnum)
                {
                    field.Type    = SPFieldType.Choice;
                    field.Choices = Enum.GetNames(argumentType);
                    if (field.Choices.Any() == false)
                    {
                        throw new SharepointCommonException("enum must have at least one field");
                    }
                    return(field);
                }
            }

            if (propType == typeof(Person))
            {
                throw new SharepointCommonException("Cannot use [Person] as mapped property. Use [User] instead.");
            }

            throw new SharepointCommonException("no field type mapping found");
        }
        internal static void ToItem <T>(T entity, SPListItem listItem, List <string> propertiesToSet = null)
        {
            var itemType = entity.GetType();
            var props    = itemType.GetProperties();

            foreach (PropertyInfo prop in props)
            {
                if (CommonHelper.IsPropertyNotMapped(prop))
                {
                    continue;
                }

                Assert.IsPropertyVirtual(prop);

                if (propertiesToSet != null && propertiesToSet.Count > 0)
                {
                    if (propertiesToSet.Contains(prop.Name) == false)
                    {
                        continue;
                    }
                }

                string spName;

                // var fieldAttrs = prop.GetCustomAttributes(typeof(FieldAttribute), false);
                var fieldAttrs = Attribute.GetCustomAttributes(prop, typeof(FieldAttribute));
                CustomFieldProvider customFieldProvider = null;
                if (fieldAttrs.Length != 0)
                {
                    spName = ((FieldAttribute)fieldAttrs[0]).Name;
                    if (spName == null)
                    {
                        spName = prop.Name;
                    }
                    customFieldProvider = ((FieldAttribute)fieldAttrs[0]).FieldProvider;
                }
                else
                {
                    spName = FieldMapper.TranslateToFieldName(prop.Name);
                }
                if (FieldMapper.IsReadOnlyField(spName) == false)
                {
                    continue;                                               // skip fields that cant be set
                }
                var propValue = prop.GetValue(entity, null);

                if (propValue == null)
                {
                    listItem[spName] = null;
                    continue;
                }

                if (prop.PropertyType == typeof(string))
                {
                    listItem[spName] = propValue;
                    continue;
                }

                if (prop.PropertyType == typeof(DateTime))
                {
                    // update DateTime field with empty value thrown exception
                    if (((DateTime)propValue) != DateTime.MinValue)
                    {
                        listItem[spName] = propValue;
                    }
                    continue;
                }

                if (prop.PropertyType == typeof(User))
                {
                    // domain user or group
                    Assert.IsPropertyVirtual(prop);

                    var user = (User)propValue;

                    SPFieldUserValue spUserValue = FieldMapper.ToUserValue(user, listItem.Web);

                    listItem[spName] = spUserValue;

                    continue;
                }

                // handle lookup fields
                if (typeof(Item).IsAssignableFrom(prop.PropertyType))
                {
                    Assert.IsPropertyVirtual(prop);

                    if (customFieldProvider == null)
                    {
                        var item   = (Item)propValue;
                        var lookup = new SPFieldLookupValue(item.Id, item.Title);
                        listItem[spName] = lookup;
                    }
                    else
                    {
                        var providerType = customFieldProvider.GetType();
                        var method       = providerType.GetMethod("SetLookupItem");

                        if (method.DeclaringType == typeof(CustomFieldProvider))
                        {
                            throw new SharepointCommonException(string.Format("Must override 'SetLookupItem' in {0} to get custom lookups field working.", providerType));
                        }

                        var value = customFieldProvider.SetLookupItem(propValue);
                        listItem[spName] = value;
                    }
                    continue;
                }

                //// handle multivalue fields
                if (CommonHelper.ImplementsOpenGenericInterface(prop.PropertyType, typeof(IEnumerable <>)))
                {
                    Assert.IsPropertyVirtual(prop);

                    Type argumentType = prop.PropertyType.GetGenericArguments()[0];

                    if (argumentType == typeof(User))
                    {
                        var users = propValue as IEnumerable <User>;
                        Assert.NotNull(users);

                        var values = new SPFieldUserValueCollection();

                        foreach (User user in users)
                        {
                            if (user is Person)
                            {   // domain user or group
                                var    person = (Person)user;
                                SPUser spUser = null;
                                try
                                {
                                    spUser = listItem.ParentList.ParentWeb.SiteUsers[person.Login];
                                }
                                catch (SPException)
                                {
                                    throw new SharepointCommonException(string.Format("User {0} not found.", user.Id));
                                }

                                var val = new SPFieldUserValue();
                                val.LookupId = spUser.ID;
                                values.Add(val);
                            }
                            else
                            {   // sharepoint group
                                SPGroup spGroup = null;
                                try
                                {
                                    spGroup = listItem.ParentList.ParentWeb.SiteGroups[user.Name];
                                }
                                catch (SPException)
                                {
                                    throw new SharepointCommonException(string.Format("Group {0} not found.", user.Name));
                                }

                                var val = new SPFieldUserValue();
                                val.LookupId = spGroup.ID;
                                values.Add(val);
                            }
                        }

                        listItem[spName] = values;
                    }

                    if (typeof(Item).IsAssignableFrom(argumentType))
                    {
                        var lookupvalues = propValue as IEnumerable;

                        if (customFieldProvider == null)
                        {
                            var spLookupValues = new SPFieldLookupValueCollection();

                            foreach (Item lookupvalue in lookupvalues)
                            {
                                var val = new SPFieldLookupValue();
                                val.LookupId = lookupvalue.Id;
                                spLookupValues.Add(val);
                            }

                            listItem[spName] = spLookupValues;
                        }
                        else
                        {
                            var providerType = customFieldProvider.GetType();
                            var method       = providerType.GetMethod("SetLookupItem");

                            if (method.DeclaringType == typeof(CustomFieldProvider))
                            {
                                throw new SharepointCommonException(string.Format("Must override 'SetLookupItem' in {0} to get custom lookups field working.", providerType));
                            }

                            var values = customFieldProvider.SetLookupItem(propValue);
                            listItem[spName] = values;
                        }
                    }
                    continue;
                }

                if (prop.PropertyType.IsEnum)
                {
                    listItem[spName] = EnumMapper.ToItem(prop.PropertyType, propValue);
                    continue;
                }

                var innerType = Nullable.GetUnderlyingType(prop.PropertyType);

                if (innerType != null && innerType.IsEnum)
                {
                    listItem[spName] = EnumMapper.ToItem(innerType, propValue);
                    continue;
                }

                if (prop.PropertyType == typeof(Person))
                {
                    throw new SharepointCommonException("Cannot use [Person] as mapped property. Use [User] instead.");
                }

                listItem[spName] = propValue;
            }
        }