Columns associated with mappings have been deleted/renamed

If you are using Linq to Sharepoint chances are that you have seen this error before. This errors usually means that either you are using a wrong list name or wrong web url in your Context contructor or there is a mismatch between the SPMetal mappings and the actual field internal names in the list. A cause for this is among other things something I blogged a while back.

Problem with this error is that it does not provide a clue on which field is the culprit, which for long lists is problematic as you will have to check each field internal name one and check against the fields mapped by SPMetal (Microsoft.SharePoint.Linq.ColumnAttribute, Name property).

So using reflector and inspecting the linq to sharepoint API i have developed my own map checking that provides me an indication on which field the mapping is failing. Here is the code:

    public static class EntityListExtensions
    {
        public static void ValidateMappings<T>(this EntityList<T> list)
        {
            var mit = Type.GetType("Microsoft.SharePoint.Linq.SPItemMappingInfo, Microsoft.SharePoint.Linq, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
            var pm = Type.GetType("Microsoft.SharePoint.Linq.PropertyMap, Microsoft.SharePoint.Linq, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");

            ConstructorInfo ctor = mit.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(Type) }, null);
            object instance = ctor.Invoke(new object[] { typeof(T) });
            var properties = (IEnumerable)mit.GetProperty("Properties").GetValue(instance, new object[] { });

            var iName = pm.GetProperty("InternalName");
            var aType = pm.GetProperty("AssociationType");
            var fType = pm.GetProperty("FieldType");

            var entityPropertyList = new List<PropertyInfo>();

            foreach (var property in properties)
            {
                var type = (AssociationType)aType.GetValue(property, new object[] { });
                if (type != AssociationType.Backward)
                {
                    var prop = (string)iName.GetValue(property, new object[] { });
                    var ft = fType.GetValue(property, new object[] { });

                    entityPropertyList.Add(new PropertyInfo { InternalName = prop, Type = ft.ToString() });
                }
            }

            var listPropertyList = new List<PropertyInfo>();
            var spDataList = list.GetType().GetField("list", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(list);

            //check if lists exists in web

            var dc = (DataContext)list.GetType().GetField("dc", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(list);
            var listName = spDataList.GetType().GetProperty("Name").GetValue(spDataList, new object[] { });

            using (SPSite site = new SPSite(dc.Web))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    if (!web.Lists.Cast<SPList>().Any(l => Equals(l.Title, listName)))
                    {
                        throw new InvalidOperationException("Could not find List '" + listName + "' in " + web.Url);
                    }
                }
            }

            IEnumerable dataFields = (IEnumerable)spDataList.GetType().GetProperty("Fields").GetValue(spDataList, new object[] { });
            foreach (var dataField in dataFields)
            {
                var prop = dataField.GetType().GetProperty("InternalName").GetValue(dataField, new object[] { }).ToString();
                var ft = dataField.GetType().GetProperty("FieldType").GetValue(dataField, new object[] { }).ToString();
                listPropertyList.Add(new PropertyInfo { InternalName = prop, Type = ft.ToString() });
            }

            //Check Mappings:

            foreach (PropertyInfo sp in entityPropertyList)
            {
                var tp = listPropertyList.FirstOrDefault(i => i.InternalName == sp.InternalName);

                if (tp == null)
                {
                    throw new InvalidOperationException("Could not find Property '" + sp.InternalName + "' in list '" + listName + "'");
                }

                if (tp.Type != sp.Type)
                {
                    throw new InvalidOperationException("Property '" + sp.InternalName + "' is " + sp.Type + " but the column in the list is set as " + tp.Type);
                }
            }
        }

        public class PropertyInfo
        {
            public string InternalName { get; set; }
            public string Type { get; set; }
        }
    }

To use it:

using (var context = new ModelDataContext( Web_Url_Here )
{
         context.EntityList_Name_Here.ValidateMappings();
}

NOTE: The code above uses reflection heavily so it is breaking Microsoft black box, therefore I cannot guarantee that future updates wont break it. And it is more than likely that it wont work with 2013 (haven’t tested it)

Leave a Reply

Your email address will not be published. Required fields are marked *

*

HTML tags are not allowed.