5

We have a Sitecore 8.1 Update-3 instance. We have created custom xDB facets. We have a modal pop-up screen that a user can use to update their custom information. This pop-up screen uses web service calls to pass the information back to the server to be saved.

I can't figure out how to properly save the xDB data in the web service. I believe under normal conditions (like in code behind) if the current user is logged in then my code should update Tracker.Current.Contact with the information. That way all of the contact info will be saved to xDB when the user's session ends. But since this code is running in a web service it doesn't have any access to Tracker.Current.Contact.

I could save it directly to xDB using custom code. But that won't work because I believe that when the person's session ends then the system will write their Tracker.Current.Contact to xDB and it will overwrite the previously saved information.

So how would I save this info to the person's contact record properly from a web service?

  • What kind of web service are you using? – Ben Golden Dec 16 '16 at 16:25
  • I didn't write it. I believe is an ASMX web service (if that is the correct terminology) – Corey Burnett Dec 16 '16 at 16:30
  • Do i understand it correctly that you don't know whether or not the contact has a live session at the moment of running this logic? – Dmytro Shevchenko Dec 16 '16 at 18:20
  • 1
    The user should definitely have a live session. They have logged in. And they have gone to our My Account page. And they click on the button to edit their information and it opens a modal pop-up. Then we are using an ASMX web service to update their information. – Corey Burnett Dec 16 '16 at 18:22
5

Since you are sure that the contact is currently in a session, you should not read or write anything directly to and from the Collection Database.

You should use the ContactManager like this:

ContactManager contactManager = Factory.CreateObject("tracking/contactManager", true) as ContactManager;

LockAttemptResult<Contact> lockAttemptResult = contactManager.TryLoadContact(contactId);

// the lock attempt will always be successful, no need to check the status

Contact contact = lockAttemptResult.Object;

// ... update the facets

contactManager.SaveAndReleaseContact(contact);

The method SaveAndReleaseContact will release the contact back to the Shared Session. It will be saved to the Collection Database at the end of the session.

Note that when you call contactManager.TryLoadContact(...), the method can only return an unsuccessful lock attempt when the contact is not in the Shared Session and it's being locked in the Collection DB. This is not the case here. Since the contact is already in the Shared Session, it will be successfully returned. If it's currently locked in the Shared Session by another thread, your current thread will just wait until it's released, and you'll be able to work with the contact.

| improve this answer | |
  • I believe that it is better to check attempt result even if it always returns true in current Sitecore version. – Daniil Dec 18 '16 at 16:50
  • @Daniil the LockAttemptResult.Status only makes sense when you try to lock the contact in the Collection Database. All session implementations use a completely different approach to locking. – Dmytro Shevchenko Dec 18 '16 at 16:55
  • @DmytroShevchenko is there any way that I can retrieve the contact using only the identifier (email address)? I tried using contactManager.LoadContactReadOnly(identifier) but that seems to pull the contact from xDB instead of Shared Session. I need to find a way to pull the contact from Shared Session when all I know is the identifier. – Corey Burnett Dec 19 '16 at 15:28
  • @CoreyBurnett you can first contactRepository.LoadContactReadOnly(identifier) to find out the ID from the identifier. And then use sharedSessionManager.LockAndLoadContact(contactId) to load the contact from session by ID. Check out my answer here for a more elaborate example. – Dmytro Shevchenko Dec 19 '16 at 15:53
2

I would recommend using a normal MVC controller (Not API controller). The MVC controller will see the user as the normal user via the tracker cookie in their browser. Then just set their details.

[HttpPost]
public JsonResult SetUserContact(UserInfo userInfo)
{
    IContactPersonalInfo personalFacet = Tracker.Current.Contact.GetFacet<IContactPersonalInfo>("Personal");
    personalFacet.FirstName = UserInfo.FirstName;
    personalFacet.Surname = UserInfo.LastName;

    IContactEmailAddresses addressesFacet = Tracker.Current.Contact.GetFacet<IContactEmailAddresses>("Emails");
    IEmailAddress address;
    if (!addressesFacet.Entries.Contains("work_email"))
    {
        address = addressesFacet.Entries.Create("work_email");
        address.SmtpAddress = UserInfo.Email;
        addressesFacet.Preferred = "work_email";
    }    
}
| improve this answer | |
  • This solution uses WebForms so that approach won't work. – Corey Burnett Dec 16 '16 at 15:50
  • Not to push it too far, but you can run MVC and Forms together. The project I am currently on, its webforms for Sitecore work and MVC for all my API/React work. I can give you some code on that if you like. – Chris Auer Dec 16 '16 at 15:54
2

You can use ContactManager for that, something like this:

var contactManager = Factory.CreateObject("tracking/contactManager", true) as ContactManager;

var lockAttemptResult = contactManager.TryLoadContact(contact.ContactId);

   if(lockAttemptResult.Status == LockAttemptStatus.Success)
   {
          var lockedContact = lockAttemptResult.Object;

          // edit contact

          manager.FlushContactToXdb(contact);
          manager.SaveAndReleaseContactToXdb(contact);
    }

but with such approach you will have to solve contact locking and session update. It is better to use regular controller and edit session contact.

| improve this answer | |
  • Saving it to the xDB like you show won't work. Because later when the user's session expires the information that I previously saved to xDB will be overwritten by the information that is in session. – Corey Burnett Dec 16 '16 at 15:52
  • Why would you flush the contact, then save and release it? First off, you're saving it twice. Second, the contact will NOT be removed from the Shared Session this way, but it will be released in the Collection Database. I don't think this is a good approach, it will definitely lead to errors. Don't use it in production. – Dmytro Shevchenko Dec 16 '16 at 18:14
  • @DmytroShevchenko we actually had to call both save methods in our scenario (which pretty complex and is not typical) and regarding second point - for sure additional code required as I mentioned in the answer. But all these needed in case when you cannot get a session contact, but I believe situation in the question can be solved with session contact. – Daniil Dec 18 '16 at 16:46
  • 1
    OK, so just to make it clear. FlushContactToXdb saves the current state of the contact to the Collection Database. SaveAndReleaseContactToXdb does the same and it also releases the lock on the contact. Hence, FlushContactToXdb is completely unnecessary—it just adds an extra database operation that doesn't help anything. Second—the OP says the contact is for sure in the session. Your code saves it to the database and releases it. The session will still contain the contact though, which will later be attempted to be saved to the database with old data. This is wrong for many reasons! – Dmytro Shevchenko Dec 18 '16 at 18:27

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.