2

I'm attempting to write a custom facet to a contact in Sitecore 10.1, and I keep getting the following error:

Unknown type
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: Unknown type

[InvalidOperationException: Unknown type]
Sitecore.Xdb.Common.Web.Synchronous.SynchronousExtensions.SuspendContextLock(Func`1 taskFactory) +490
Sitecore.XConnect.Client.XConnectSynchronousExtensions.SuspendContextLock(Func`1 taskFactory) +20
Sitecore.MyCompany.Foundation.AgencyData.CustomSitecore.Facets.ContactFacetManager.SetFacet(String facetName, T facet, Contact contact) +310
[... stack trace continues]

As far as I know I have done the following correctly (but I'll provide as much code below as I can):

  • Created the facet and XConnect model
  • Deployed the model to the XConnect server, including serializing the model to JSON and copying it to both locations
  • Copied the .dll to the XConnect server in addition to deploying it to the single-instance server (this is my local installation)
  • Created a config that adds the dataAdapterManager/providers/add/facets/facet node as well as the xconnect/runtime/schemas/schema node

I've debugged as much as I can, making sure to load symbols for Sitecore.XConnect, Sitecore.XConnect.Client, and Sitecore.Xdb.Common.Web. The error seems to come from the scheduler.ProcessTaskQueue() line in Sitecore.Xdb.Common.Web.Synchronous.SynchronousExtensions.SuspendContextLock but I'm having trouble figuring it out from there.

The facet is being set in a custom pipeline that's triggered from the mvc.requestBegin pipeline (i.e., there's a processor in that pipeline that kicks off a separate pipeline to deal with this facet). I'm quite certain there's something stupid I've overlooked but I have no idea what it is.

AssociatedAgentFacet.cs

[Serializable]
[FacetKey(DefaultFacetKey)]
public class AssociatedAgentFacet : XConnect.Facet, IFacet
{
    public const string DefaultFacetKey = "AssociatedAgent";
    public IAgencyDataEntity Agency { get; set; }
       
    public void Reset()
    {
        Agency = new AgencyDataEntity();
    }

    public bool IsEmpty
    {
        get
        {
            return !Agency.IsValid();
        }
    }

    public void Validate()
    {
        throw new NotImplementedException();
    }
}

CollectionModel.cs

public class CollectionModel
{
    public static XdbModel Model { get; } = BuildModel();

    private static XdbModel BuildModel()
    {
        XdbModelBuilder builder = new XdbModelBuilder("AgencyDataXConnectModel", new XdbModelVersion(1, 0));
        builder.ReferenceModel(XConnect.Collection.Model.CollectionModel.Model);
        builder.DefineFacet<Contact, AssociatedAgentFacet>(AssociatedAgentFacet.DefaultFacetKey);

        return builder.BuildModel();
    }
}

AssociateStubAgency.cs (processor on custom pipeline)

public class AssociateStubAgency : AgencyRetrieveAssociationPipelineProcessor
{
    public override void Process(AgencyRetrieveAssociationPipelineArgs args)
    {
        Log.Info("[AssociateStubAgency] begin", this);
        if (Tracker.Current == null)
        {
            args.AbortPipeline();
            return;
        }

        AssociatedAgentFacet agencyFacet = new AssociatedAgentFacet
        {
            Agency = new AgencyDataEntity
            {
                AgencyName = "Stub Agency"
            }
        };
        args.Agency = agencyFacet.Agency;

        using (ContactFacetManager contactFacetManager = new ContactFacetManager())
        {
            contactFacetManager.SetFacet(AssociatedAgentFacet.DefaultFacetKey, agencyFacet);
        }
    }
}

ContactFacetManager.cs

public class ContactFacetManager : IDisposable
{
    private bool _disposedValue;

    private XConnectClient Client { get; }
    private ContactManager _contactManager;
    private Contact CurrentContact { get; set; }

    public ContactFacetManager()
    {
        Client = SitecoreXConnectClientConfiguration.GetClient();
        SetContact();
    }

    public void SetFacet<T>(string facetName, T facet) where T : Facet
    {
        SetContact(facetName);
        SetFacet(facetName, facet, CurrentContact);
    }

    public void SetFacet<T>(string facetName, T facet, Contact contact) where T : Facet
    {
        Assert.IsNotNull(contact, "Contact is null.");

        if (HasFacet(contact, facetName))
        {
            UpdateCurrentFacet(contact, facetName, facet);
        }
        else
        {
            AddNewFacet(contact, facetName, facet);
        }

        Client.Submit();
    }

    private void UpdateCurrentFacet<T>(Contact contact, string facetName, T facet) where T : Facet
    {
        Client.SetFacet(contact, facetName, facet);
    }

    private void AddNewFacet<T>(Contact contact, string facetName, T facet) where T : Facet
    {
        Client.SetFacet(contact, facetName, facet);
    }

    private bool HasFacet(Contact contact, string facetName)
    {
        return contact.Facets.ContainsKey(facetName);
    }

    private void SetContact(string facetName = null)
    {
        _contactManager =
            Configuration.Factory.CreateObject("tracking/contactManager", true) as ContactManager;
        if (_contactManager != null)
        {
            Analytics.Tracker.Current.Contact.ContactSaveMode = ContactSaveMode.AlwaysSave;
            _contactManager.SaveContactToCollectionDb(Analytics.Tracker.Current.Contact);
            IdentifiedContactReference reference = new IdentifiedContactReference(
                Analytics.XConnect.DataAccess.Constants.IdentifierSource,
                Analytics.Tracker.Current.Contact.ContactId.ToString("N"));
            CurrentContact = Client.Get(reference, new ContactExecutionOptions(
                string.IsNullOrEmpty(facetName) ? new ContactExpandOptions() : new ContactExpandOptions(facetName)
            ));
        }
    }

    #region IDisposable

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposedValue)
        {
            if (disposing)
            {
                Client.Dispose();
            }

            _disposedValue = true;
        }
    }

    public void Dispose()
    {
        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }

    #endregion
}

Foundation.AgencyData.config

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <pipelines>
            <mvc.requestBegin>
                <processor type="Sitecore.MyCompany.Foundation.AgencyData.CustomSitecore.Pipelines.Processors.InvokeRetrieveAssociationPipeline,Sitecore.MyCompany.Foundation.AgencyData" patch:after="processor[@type='Sitecore.Mvc.Analytics.Pipelines.MvcEvents.RequestBegin.StartTracking, Sitecore.Mvc.Analytics']" />
            </mvc.requestBegin>
            <agency.RetrieveAssociation>
                <processor type="Sitecore.MyCompany.Foundation.AgencyData.CustomSitecore.Pipelines.Processors.CheckExistingAssociation,Sitecore.MyCompany.Foundation.AgencyData" />
                <processor type="Sitecore.MyCompany.Foundation.AgencyData.CustomSitecore.Pipelines.Processors.AssociateStubAgency,Sitecore.MyCompany.Foundation.AgencyData" />
            </agency.RetrieveAssociation>
        </pipelines>
        <dataAdapterManager defaultProvider="xconnect">
            <providers>
                <add name="xconnect" type="Sitecore.Analytics.XConnect.DataAccess.XConnectDataAdapterProvider, Sitecore.Analytics.XConnect">
                    <facets hint="raw:AddFacet">
                        <facet facetKey="AssociatedAgent" />
                    </facets>
                </add>
            </providers>
        </dataAdapterManager>
        <xconnect>
            <runtime type="Sitecore.XConnect.Client.Configuration.RuntimeModelConfiguration,Sitecore.XConnect.Client.Configuration">
                <schemas hint="list:AddModelConfiguration">
                    <schema name="AgencyDataXConnectModel" type="Sitecore.XConnect.Client.Configuration.StaticModelConfiguration,Sitecore.XConnect.Client.Configuration">
                        <param desc="modeltype">Sitecore.MyCompany.Foundation.AgencyData.Models.CollectionModel,Sitecore.MyCompany.Foundation.AgencyData</param>
                    </schema>
                </schemas>
            </runtime>
        </xconnect>
    </sitecore>
</configuration>

AgencyDataXConnectModal, 1.0.json

{
  "Name": "AgencyDataXConnectModel",
  "Version": "1.0",
  "References": [
    {
      "Name": "XConnect",
      "Version": "1.0"
    },
    {
      "Name": "Sitecore.XConnect.Collection.Model",
      "Version": "10.1"
    }
  ],
  "Types": {
    "Sitecore.MyCompany.Foundation.AgencyData.CustomSitecore.Facets.AssociatedAgentFacet": {
      "Type": "Facet",
      "BaseType": "Sitecore.XConnect.Facet",
      "ClrType": "Sitecore.MyCompany.Foundation.AgencyData.CustomSitecore.Facets.AssociatedAgentFacet, Sitecore.MyCompany.Foundation.AgencyData, Version=3.2.4.0, Culture=neutral, PublicKeyToken=null",
      "Properties": {
        "Agency": {
          "Type": "Sitecore.MyCompany.Foundation.AgencyData.Models.IAgencyDataEntity"
        }
      }
    },
 
"Sitecore.MyCompany.Foundation.AgencyData.Models.IAgencyDataEntity": {
      "Type": "Complex",
      "Abstract": true,
      "ClrType": "Sitecore.MyCompany.Foundation.AgencyData.Models.IAgencyDataEntity, Sitecore.MyCompany.Foundation.AgencyData, Version=3.2.4.0, Culture=neutral, PublicKeyToken=null",
      "Properties": {
        "AgencyName": {
          "Type": "String"
        }
      }
    }
  },
  "Facets": [
    {
      "Target": "Contact",
      "Name": "AssociatedAgent",
      "Type": "Sitecore.MyCompany.Foundation.AgencyData.CustomSitecore.Facets.AssociatedAgentFacet"
    }
  ]
}
16
  • Did you patch the config file (<Instance_Name>.sc\App_Config\Sitecore\XConnect.Client.Configuration) to register custom CollectionModel? Jun 19 at 14:04
  • Try adding an empty constructor to the new facet. According to docs, it's needed: doc.sitecore.com/developers/90/sitecore-experience-platform/en/…
    – grg
    Jun 21 at 8:40
  • @RamanGupta I have that in a patch config. I've added that to the question for reference. Jun 21 at 12:58
  • @grg I had it in at one point but Resharper said it was redundant, so I took it out. Added it back in just to check but it didn't make a difference. Jun 21 at 12:58
  • 1
    I suppose that issue occures because of you have XConnectClient as a valiable and it catch locks. Try to rewrite your code by wrapping with using (var client = SitecoreXConnectClientConfiguration.GetClient())
    – x3mxray
    Jun 23 at 7:55
2

Make sure that your entire facet object is serializable. Try replacing the interface reference with a concrete class, and make sure that class is marked [Serializable]. That will let the serialization process, and especially the deserialization process, know how to convert the in memory objects into data and vice versa.

So, in AssociatedAgentFacet.cs, replace

public IAgencyDataEntity Agency { get; set; }

with

public AgencyDataEntity Agency { get; set; }

And make sure that AgencyDataEntity has the [Serializeable] attribute, as long with any classes it references, and so forth until you get down to primitive types.

1
  • This has resolved the Unknown Type issue. I still don't see the facet showing up in the database so I don't think I'm entirely out of the woods yet, but it's probably an exciting new problem. Marking this as the answer. Jul 22 at 19:46
1

I am not able to find in your code, where you are Deploying your Custom Facet model to XDB. I think you forget this step.

Deploy custom Facet model to XDB: To Deploy the custom facet model to XDB you need to serialize the collection model in which you defined your custom facet model. To serialize into JSON prefer Console application to generate the JSON file.

class Program
{
    static void Main(string[] args)
    {
        var serlizableModel = XdbModelWriter.Serialize(CustomFacetCollectionModel.Model);
        File.WriteAllText(CustomFacetCollectionModel.Model.FullName + ".json", serlizableModel);
    }
}

On execution of the console application, it will generate a JSON file. Copy that JSON file and paste at the below places:

  • x-connect root path > App_data/Models
  • x-connect root path > App_data/jobs/continuous/IndexWorker/App_data/Models

Refer Step 3 in Blog Post: Create Custom Facet in Sitecore

Official Documentation: https://doc.sitecore.com/developers/90/sitecore-experience-platform/en/deploy-a-custom-model.html

I have implemented the same functionality in the Sitecore 9.3, So I am explaining the same approach here as below:

Create Custom Facet in Sitecore: To Create custom facets we need to perform the below-mentioned steps:

  1. Create a Custom Facet Model
  2. Register the Custom Facet Model
  3. Deploy custom Facet model to XDB
  4. Add Custom Facet Model to Configuration

The above-mentioned steps will help you to create Custom Facets into the Sitecore and you can use those. For a detailed description please refer to the Blog Post: Create Custom Facet in Sitecore

Work with Custom Facet in Sitecore:

  1. Add a new custom facet
  2. Fetch and update custom facet

For the detailed description of the above functionality, please refer to Blog Post: Work with Custom Facet in Sitecore

Could you please refer above articles to check if you are missing any steps and configuration? Hope it helps you.

4
  • Yes, I have already done those steps. Jun 22 at 18:33
  • In your above code, I am not able to find where you are deploying the custom model. I am not sure, but could you please check if you are missing this step. Jun 23 at 3:57
  • I have updated the answer. Jun 23 at 4:09
  • I didn't include the serialization code in the original listing because it's pretty boilerplate, but I did do that step. I mentioned it in the question in the "as far as I know I've done the following correctly" bulleted list. Jun 23 at 13:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.