4

I have a task to convert the address that was Synced from CRM into Sitecore item upon running the Sync in Data Exchange. I am new to this feature of Sitecore and was thinking of creating a pipeline during the process of Synchronization from the Data Exchange to convert the address from CRM into a latitude and longitude and map it into a GeoLocation field we have in Sitecore Template.

I just like to have an idea on how to do this and what are the things I need.

Thanks in advance.

2
  • Thanks for your solutions, I tried to use your code but when I debuging, it's not going to Convert method hit breakpoint in my ValueAccessorConverter class. After manually execute Batch, log message have ERROR is: ERROR Pipeline step processing will abort because mapping set failed Thanks for help.
    – Anh Quan
    Nov 8, 2017 at 2:45
  • @AnhQuan - if you have another question to ask, please open a new question. Do not add new questions to comments.
    – Richard Seal
    Nov 8, 2017 at 9:16

1 Answer 1

8

I can suggest the following solution:

You need to implement a custom field reader with a custom field converter:

using Sitecore.DataExchange.DataAccess;

namespace Example
{
    public interface IBaseValueReader: IValueReader
    {
        string AttributeName { get; set; }
    }
}

AddressFieldReader:

using System;
using Sitecore.DataExchange.DataAccess;
using Sitecore.DataExchange.Repositories;

namespace Example
{
    public class AddressFieldReader : IBaseValueReader
    {
        public string AttributeName { get; set; }
        public IItemModelRepository Repository { get; private set; }
        public ExampleFieldReader(IItemModelRepository repository)
        {
            Repository = repository;
        }

        public CanReadResult CanRead(object source, DataAccessContext context)
        {
            return new CanReadResult()
            {
                CanReadValue = true
            };
        }

        public ReadResult Read(object source, DataAccessContext context)
        {
            object result = (object)null;

            var sourceObject = source as Microsoft.Xrm.Sdk.Entity;
            if (sourceObject != null && AttributeName != null)
            {
                object value;

                //Try to read attribute by the attribute name 

                if (sourceObject.Attributes.TryGetValue(AttributeName, out value))
                {

                    //Then you need to cast it to the address field type

                    var searchObject = value as Microsoft.Xrm.Sdk.Money; //It is example. You need to debug and see which type you exactly get. 

                    //Then you need to check if field is not null and have value.

                    if (searchObject != null && searchObject.Value != Decimal.Zero)
                    {
                        var fieldValue = searchObject.Value;

                        //Now, when you got a value of field, you can use any API to get GeoLocation. 
                        // Your code here:

                        var geoLocation = fieldValue; 

                        //Then you should return result:

                        return new ReadResult(DateTime.UtcNow)
                        {
                            WasValueRead = true,
                            ReadValue = geoLocation
                        };
                    }
                }

            }

            return new ReadResult(DateTime.UtcNow)
            {
                WasValueRead = true,
                ReadValue = String.Empty
            };
        }
    }
}

AddressFieldReaderConverter:

using Sitecore.DataExchange.DataAccess;
using Sitecore.DataExchange.Extensions;
using Sitecore.DataExchange.Repositories;
using Sitecore.Services.Core.Model;
using System;
using Sitecore.DataExchange.Converters;

namespace Example
{
    public class AddressFieldReaderConverter : BaseItemModelConverter<ItemModel, IValueReader>
    {
        private AddressFieldReader _reader = (AddressFieldReader)null;


        private static readonly Guid TemplateId = Guid.Parse("{C3C9E93C-F197-4FBD-820D-DCB565707AF7}");
        public AddressFieldReaderConverter(IItemModelRepository repository)
      : base(repository)
        {
            this.SupportedTemplateIds.Add(TemplateId);
        }

        public override bool CanConvert(ItemModel source)
        {
            return true;
        }

        public override IValueReader Convert(ItemModel source)
        {
            if (source == null)
            {
                Sitecore.DataExchange.Context.Logger.Error("Cannot convert null item to value reader. (converter: {0})", (object)this.GetType().FullName);
                return (IValueReader)null;
            }
            if (!this.CanConvert(source))
            {
                Sitecore.DataExchange.Context.Logger.Error("Cannot convert item to value reader. (item: {0}, converter: {1})", (object)source.GetItemId(), (object)this.GetType().FullName);
                return (IValueReader)null;

            }

            if (_reader == null)
                _reader = new AddressFieldReader(ItemModelRepository);
            return (IValueReader)_reader;
        }
    }
}

You also need to implement a Value Accessor for passing the Attribute Name Value from the Value Accessor to the Field Reader:

using Sitecore.DataExchange.Converters.DataAccess.ValueAccessors;
using Sitecore.DataExchange.DataAccess;
using Sitecore.DataExchange.DataAccess.Writers;
using Sitecore.DataExchange.Providers.DynamicsCrm.DataAccess.Readers;
using Sitecore.DataExchange.Repositories;
using Sitecore.Services.Core.Model;
using System;

namespace Example
{
    public class CustomValueAccessorConverter : ValueAccessorConverter
    {
        private static readonly Guid TemplateId = Guid.Parse("{C3C9E93C-F197-4FBD-820D-DCB565707AF7}");

        public LookupValueAccessorConverter(IItemModelRepository repository)
          : base(repository)
        {
            this.SupportedTemplateIds.Add(LookupValueAccessorConverter.TemplateId);
        }

        public override IValueAccessor Convert(ItemModel source)
        {
            IValueAccessor valueAccessor = base.Convert(source);
            if (valueAccessor == null)
                return (IValueAccessor)null;


            string stringValue = this.GetStringValue(source, "AttributeName");
            if (string.IsNullOrWhiteSpace(stringValue))
                return (IValueAccessor)null;
            bool boolValue = this.GetBoolValue(source, "UseValueProperty");



            if (valueAccessor.ValueReader == null)
            {
                EntityAttributeValueReader attributeValueReader = new EntityAttributeValueReader(stringValue);
                if (this.GetBoolValue(source, "UseValueProperty"))
                    attributeValueReader.UseValueProperty = true;
                valueAccessor.ValueReader = (IValueReader)attributeValueReader;
            }
            else
            {
                //Custom part. Initialize attribute for read

                var customFieldReader = valueAccessor.ValueReader as IBaseValueReader;
                if (customFieldReader != null)
                {
                    customFieldReader.AttributeName = stringValue;
                }
            }

            if (valueAccessor.ValueWriter == null && !boolValue)
                valueAccessor.ValueWriter = (IValueWriter)new IndexerPropertyValueWriter(new object[1]
                {
          (object) stringValue
                });

            return valueAccessor;
        }
    }
}

Then you need to create a template for AddressField Reader and inherit it from the Value Reader template (/sitecore/templates/Data Exchange/Framework/Data Access/Value Readers/Value Reader):

Create Value Reader Template Updating _StandardValues

Then, based on this template, you need to create a Value Reader Item (make sure the converter is correct):

Creating instanse of Value Reader

The next step is to create a Value Accessor for your import object (make sure the Converter type set to your custom implemented):

Create Value Accessor

Then you can use this Value Accessor in your Value Mapping Set.

Then you need to run an import and see if it is works. Take into account that it is not exactly your solution. It is just an example. You need debug and correct this code.

7
  • Thanks for your suggestion. I tried the code above and followed the instructions so I can debug it however, the breakpoints I set isn't being hit. Is there something I missed? Thanks.
    – Jovit Mayo
    Oct 10, 2017 at 3:21
  • Have you selected the crated Value Accessor for address filed in the Value Mapping for imported object? Oct 10, 2017 at 4:19
  • Yes, I have selected it.
    – Jovit Mayo
    Oct 10, 2017 at 4:36
  • Does sync work in general? Can you see imported objects? Oct 10, 2017 at 5:02
  • Yes the Sync work and the imported items from CRM are created in Sitecore. I can't find any hints where would I check to have it hit during debugging.
    – Jovit Mayo
    Oct 10, 2017 at 5:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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