Deleting missing metafields in code programmatically
This blog post will help you in deleting meta fields programatically in catalogue context. We will only delete metafields which are user generated and missing from code (deleted later in the code) but remain in database.
#1 - get the list of metaclasses (user generated)
private readonly Mediachase.MetaDataPlus.Configurator.MetaClassCollection _metaClassCollection;
_metaClassCollection = Mediachase.MetaDataPlus.Configurator.MetaClass.GetList(CatalogContext.MetaDataContext, true);
_metaClassCollection.Cast<Mediachase.MetaDataPlus.Configurator.MetaClass>()
.Where(c => c.IsUser
&& c.MetaFields.Any(x => x.IsUser))
The above collection will fetch meta classes created by the user and any of it's metafields is user generated at least.
#2 - Getting metafields for class
metaClass.MetaFields
.Where(c => c.IsUser && c.OwnerMetaClassIdList.MetaCollectionHasOnly(id))
Above code snippet selects all the meta fields for a metaclass and makes sure it is not fetching any other metafield under a class which comes from relation of the same class (i.e. product > Category > Catalog)
Therefore, It only displays meta fields strictly which are part of the class. I've used an extension method that makes sure each metafiled has only 1 exact same parent as current class.
public static bool MetaCollectionHasOnly(this MetaClassIdCollection collection, int classId)
{
try
{
if (collection != null && collection.Count == 1)
{
return collection[0] == classId;
}
return false;
}
catch
{
//we don't want to catch exception in this case.
return false;
}
}
Now it comes to the final step which is most important - Identify missing field from class
Each metaclass is mapped with contentType class you have in your code. Here we need to figure out which class in code is mapped with metaclass in database. Let's understand it easy way -
If I have a base class for all the products in the code as ProductBase with [CatalogContentType] decorator then for MetaClass Product MetaClassToContentTypeMap will fetch ProductBase.
Now the only thing you need to check if the metafield exists in the code as follows -
private readonly Injected<MetaClassToContentTypeMap> _contentTypeModelRepository;
public string MetaFieldName { get; set; }
private Type ClassType
{
get
{
return _contentTypeModelRepository.Service.GetContentTypeModel(MetaClassId);
}
}
public bool IsAvailable
{
get
{
return ClassType.HasProperty(this.MetaFieldName);
}
}
IsAvailable property returns whether the metafield exists in the code or not.
--------------------------------------------------------------
Update - Deleting what we have detected so far. In my project I had an endpoint which would do it for me. The field you want to delete, send it along with it's parent classId.
[HttpPost]
[Route("metafields/{classId}/delete/{fieldId}", Name = "DeleteMetaField")]
public JsonResult Delete(int classId, int fieldId)
{
var result = false;
try
{
var metaClass = _metaClassCollection.Cast<Mediachase.MetaDataPlus.Configurator.MetaClass>()
.FirstOrDefault(c => c.Id.Equals(classId));
if (metaClass != null)
{
var metaField = metaClass.MetaFields.FirstOrDefault(c => c.Id.Equals(fieldId));
metaField.Delete();
}
result = true;
}
catch (Exception ex)
{
Log.Error("Failed to delete" + ex.Message);
result = false;
}
return new JsonResult(result);
}
And the extension method to delete:
public static class MetaFieldExtensions
{
private static readonly MetaDataContext Context = CatalogContext.MetaDataContext;
public static void Delete(this MetaField metaField)
{
if (metaField == null)
{
return;
}
foreach (var metaClassId in metaField.OwnerMetaClassIdList)
{
var cls = Mediachase.MetaDataPlus.Configurator.MetaClass.Load(Context, (int)metaClassId);
cls.DeleteField(metaField);
}
MetaField.Delete(Context, metaField.Id);
}
}
Hope this helps.
@Manoj
Thanks for the article. In the title I see "Deleting missing metafields in code programmatically", but from the article I only learned how to detect properties that are still in the database, but not in the code. Could you extend the article with more information on how to get rid of such unused properties?
In my case, after using Commerce for a long time, I am dealing with properties that are not used - sometimes they are single, sometimes there are more. I would like them not to be displayed as well as get rid of them from the database to speed up the performance of the products - currently, by handling existing obsolete properties, the products seem to run slower.
Marcin, Just updated it. Hope it helps!