Custom taxonomies installation bug (+ solution)

Posted by Community Admin on 04-Aug-2018 17:02

Custom taxonomies installation bug (+ solution)

All Replies

Posted by Community Admin on 05-Jul-2011 00:00

Hi

This post is for Telerik developers to take care about the bug in future releases, and also I am providing workaround for developers.

BUG: in custom module when you add your own Taxonomies + standard Categories and Tags taxonomies your taxonomies are created but MetaFields are not added to your Content item class

This bug is also presented in Products module from SDK:
EXPECTED BEHAVIOR: Color property value should be saved to ProductItem entity
ACTUAL BEHAVIOR: Color property value isn't saved to ProductItem entity

Following exception is put into website log duting module installation process:

----------------------------------------
Timestamp: 05.07.2011 15:58:17
 
Message: HandlingInstanceID: 0efae780-07b1-400f-89b5-f6247094b8d2
An exception of type 'Telerik.OpenAccess.Exceptions.DuplicateKeyException' occurred and was caught.
---------------------------------------------------------------------------------------------------
07/05/2011 22:58:17
Type : Telerik.OpenAccess.Exceptions.DuplicateKeyException, Telerik.OpenAccess, Version=2011.1.510.1, Culture=neutral, PublicKeyToken=7ce17eeaf1d59342
Message : Insert of '645785618-8d43b71d-1d96-4aa9-be0f-380740b346be' failed: Telerik.OpenAccess.RT.sql.SQLException: Cannot insert duplicate key row in object 'dbo.sf_meta_types' with unique index 'idx_sf_meta_types'.
The statement has been terminated.
   at Telerik.OpenAccess.RT.Adonet2Generic.Impl.PreparedStatementImp.execute()
   at OpenAccessRuntime.Relational.conn.PooledPreparedStatement.execute()
   at OpenAccessRuntime.Relational.RelationalStorageManager.generateInserts(NewObjectOID oid, Int32 index, ClassMetaData cmd, PersistGraph graph, Int32[] fieldNos, CharBuf s, Object[] oidData, IntArray toUpdateIndexes)
INSERT INTO [sf_meta_types] ([id], [app_name], [assembly_name], [base_class_name], [class_name], [database_inheritance], [is_dynamic], [last_modified], [module_name], [name_space], [section_captions_resource_type], [voa_version]) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
(set event logging to all to see parameter values)
Source : Unity_ILEmit_DynamicClasses
Help link :
InnerExceptions : System.Exception[]
FailedObject : NewObjectOID@3 class=MetaType id=1 realOID=
BackendError : Telerik.OpenAccess.RT.sql.SQLException: Cannot insert duplicate key row in object 'dbo.sf_meta_types' with unique index 'idx_sf_meta_types'.
The statement has been terminated.
   at Telerik.OpenAccess.RT.Adonet2Generic.Impl.PreparedStatementImp.execute()
   at OpenAccessRuntime.Relational.conn.PooledPreparedStatement.execute()
   at OpenAccessRuntime.Relational.RelationalStorageManager.generateInserts(NewObjectOID oid, Int32 index, ClassMetaData cmd, PersistGraph graph, Int32[] fieldNos, CharBuf s, Object[] oidData, IntArray toUpdateIndexes)
Reason : DuplicateKey
CanRetry : False
Data : System.Collections.ListDictionaryInternal
TargetSite : Void CommitTransaction()
Stack Trace :    at DynamicModule.ns.Wrapped_OpenAccessMetaDataProvider_cee42e39aab943f7b28bfa2d226d87c5.CommitTransaction()
   at Telerik.Sitefinity.Data.ManagerBase`1.SaveChanges()
   at Telerik.Sitefinity.Services.InstallContext.SaveChanges(Boolean clean)
   at Telerik.Sitefinity.Services.SystemManager.InitializeModule(ModuleSettings settings, InstallContext installContext, Boolean start)
 
Additional Info:
 
MachineName : THINKPAD
TimeStamp : 05.07.2011 15:58:17
FullName : Telerik.Sitefinity.Utilities, Version=4.1.1395.0, Culture=neutral, PublicKeyToken=b28c218413bdf563
AppDomainName : 911f9b7b-1-129543550855244266
ThreadIdentity :
WindowsIdentity : Thinkpad\Alex
Requested URL : /default.aspx?
    Inner Exception
    ---------------
    Type : Telerik.OpenAccess.RT.sql.SQLException, Telerik.OpenAccess.Runtime, Version=2011.1.510.1, Culture=neutral, PublicKeyToken=7ce17eeaf1d59342
    Message : Cannot insert duplicate key row in object 'dbo.sf_meta_types' with unique index 'idx_sf_meta_types'.
The statement has been terminated.
    Source : Telerik.OpenAccess.Adonet2
    Help link :
    ErrorCode : 2601
    Number : 2601
    ObjectId :
    Description : SQLState=;Cannot insert duplicate key row in object 'dbo.sf_meta_types' with unique index 'idx_sf_meta_types'.
The statement has been terminated.
    Data : System.Collections.ListDictionaryInternal
    TargetSite : Boolean execute()
    Stack Trace :    at Telerik.OpenAccess.RT.Adonet2Generic.Impl.PreparedStatementImp.execute()
       at OpenAccessRuntime.Relational.conn.PooledPreparedStatement.execute()
       at OpenAccessRuntime.Relational.RelationalStorageManager.generateInserts(NewObjectOID oid, Int32 index, ClassMetaData cmd, PersistGraph graph, Int32[] fieldNos, CharBuf s, Object[] oidData, IntArray toUpdateIndexes)

Code example that produce bug (taken from Products module):

/// <summary>
        /// Installs the taxonomies.
        /// </summary>
        /// <param name="initializer">The initializer.</param>
        protected void InstallCustomTaxonomies(SiteInitializer initializer)
        
            //installs the default Tags and Category taxonomies
            this.InstallTaxonomy(initializer, typeof(ProductItem));
 
 
            var metaMan = initializer.Context.MetadataManager;
            var taxMan = initializer.TaxonomyManager;
 
            var flatTaxonomy = this.CreateTaxonomy<FlatTaxonomy>(initializer, "Colors", ColorsTaxonomyId, "Color");
 
            var taxon1 = initializer.TaxonomyManager.CreateTaxon<FlatTaxon>();
            taxon1.Title = "Red";
            taxon1.Name = "Red";
            var taxon2 = initializer.TaxonomyManager.CreateTaxon<FlatTaxon>();
            taxon2.Title = "Blue";
            taxon2.Name = "Blue";
            flatTaxonomy.Taxa.Add(taxon1);
            flatTaxonomy.Taxa.Add(taxon2);
 
            var type = metaMan.GetMetaType(typeof(ProductItem));
            if (type == null)
            
                type = metaMan.CreateMetaType(typeof(ProductItem));
            
 
            if (!type.Fields.ToList().Any(fld => fld.FieldName == "Colors"))
            
                var field = metaMan.CreateMetafield("Colors");
                field.TaxonomyProvider = taxMan.Provider.Name;
                field.TaxonomyId = ColorsTaxonomyId;
                field.IsSingleTaxon = false;
                type.Fields.Add(field);
            
 
        

I have looked thorough InstallTaxonomy method source code and found the reason of wrong behavior:

InstallTaxonomy method is working the same way as Colors taxonomy installation in InstallCustomTaxonomies, and also have analogue of following construction:
var type = metaMan.GetMetaType(typeof(ProductItem));
            if (type == null)
            
                type = metaMan.CreateMetaType(typeof(ProductItem));
            

So inside the InstallTaxonomy method metaMan.CreateMetaType is executed and new MetaType created, it is necessary if meta type doesn't exist and all seems to be right

BUT

when in our code we are executing metaMan.GetMetaType it returns null instead of expected MetaType object created inside standard InstallTaxonomy method. And our code creates the second MetaType, and our MetaField is applied to that second MetaType.

Then when all operations are commited to DB first MetaType (from InstallTaxonomy) is added and Categories, Tags meta fields applied to it. Second MetaType insertion occure Exception, and our taxonomy meta fields aren't applied of course because they are added to wrong MetaType.

OFFER FOR FUTURE RELEASES TO FIX THE ISSUE

Please allow metaMan.GetMetaType return value of added but not commited to DB MetaType

- or -

Create MetaType parameter for InstallTaxonomy method:
protected void InstallTaxonomy(SiteInitializer initializer, Type itemType, MetaType metaType);
to make developer able to declare MetaType only once

WORKAROUND

My workaround is to create method with signature
protected void InstallTaxonomy(SiteInitializer initializer, Type itemType, MetaType metaType);

which code is the same as taken from original method using Reflector but without MetaType initialization, and initialize type only once in the beginning of InstallCustomTaxonomies methos:

/// <summary>
        /// Installs the taxonomies.
        /// </summary>
        /// <param name="initializer">The initializer.</param>
        protected void InstallCustomTaxonomies(SiteInitializer initializer)
        
            var metaMan = initializer.Context.MetadataManager;
            var type = metaMan.GetMetaType(typeof(ProductItem));
            if (type == null)
            
                type = metaMan.CreateMetaType(typeof(ProductItem));
            
 
            //installs the default Tags and Category taxonomies
            this.InstallTaxonomy(initializer, typeof(ProductItem), type);
 
             
            var taxMan = initializer.TaxonomyManager;
 
            var flatTaxonomy = this.CreateTaxonomy<FlatTaxonomy>(initializer, "Colors", ColorsTaxonomyId, "Color");
 
            var taxon1 = initializer.TaxonomyManager.CreateTaxon<FlatTaxon>();
            taxon1.Title = "Red";
            taxon1.Name = "Red";
            var taxon2 = initializer.TaxonomyManager.CreateTaxon<FlatTaxon>();
            taxon2.Title = "Blue";
            taxon2.Name = "Blue";
            flatTaxonomy.Taxa.Add(taxon1);
            flatTaxonomy.Taxa.Add(taxon2);
 
            if (!type.Fields.ToList().Any(fld => fld.FieldName == "Colors"))
            
                var field = metaMan.CreateMetafield("Colors");
                field.TaxonomyProvider = taxMan.Provider.Name;
                field.TaxonomyId = ColorsTaxonomyId;
                field.IsSingleTaxon = false;
                type.Fields.Add(field);
            
 
        

Best regards,
Alex

Posted by Community Admin on 07-Jul-2011 00:00

Hello Kronos,

An issue like the one you report is not observed in the recently released Sitefinity SP2 and in the latest service pack that goes with it. Still your solution seems to be valid for older releases of Sitefinity. We actually have a fix in the metadataprovider that returns not only metatypes existing in the database but also metatypes that were added but not committed, so the old code should be working ok with the latest version of Sitefinity.

Kind regards,
Nikolay Datchev
the Telerik team

Do you want to have your say in the Sitefinity development roadmap? Do you want to know when a feature you requested is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items

This thread is closed