Compartilhar via


Sample: Collaborate with activity feeds

 

Applies To: Dynamics CRM 2013

This sample code is for Microsoft Dynamics CRM 2013 and Microsoft Dynamics CRM Online. Download the Microsoft Dynamics CRM SDK package. It can be found in the following location in the download package:

SampleCode\CS\BusinessDataModel\ActivityFeeds\WorkingWithActivityFeeds.cs
SampleCode\VB\BusinessDataModel\ActivityFeeds\WorkingWithActivityFeeds.vb

Requirements

For more information about the requirements for running the sample code provided in this SDK, see Use the sample and helper code.

Demonstrates

This sample shows how to create posts with comments and mentions and how to follow Microsoft Dynamics CRM records. It also demonstrates how to retrieve information for the personal and record walls.

Example


using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Threading;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;

namespace Microsoft.Crm.Sdk.Samples
{
    /// <summary>
    /// Demonstrates how to work with the new Activity Feeds entities, including Post,
    /// PostFollow, and PostComment.</summary>
    /// <remarks>
    /// At run-time, you will be given the option to delete all the
    /// database records created by this program.</remarks>
    public class WorkingWithActivityFeeds
    {
        #region Local Sample Types
        private class InvalidSampleExecutionException : Exception
        {
            public InvalidSampleExecutionException(String message)
                : base(message)
            {
            }
        }
        #endregion Local Sample Types

        #region Class Level Members
        private OrganizationServiceProxy _serviceProxy;
        private ServiceContext _serviceContext;
        private msdyn_PostConfig _originalLeadConfig;
        private msdyn_PostConfig _originalSystemUserConfig;
        private msdyn_PostConfig _systemuserConfig;
        private msdyn_PostConfig _leadConfig;
        private List<msdyn_PostRuleConfig> _postRuleConfigs =
            new List<msdyn_PostRuleConfig>();
        private Lead _lead1;
        private Lead _lead2;
        private Lead _lead3;
        private PostFollow _follow1;
        private PostFollow _follow2;
        private PostFollow _follow3;
        private List<EntityReference> _generatedEntities = new List<EntityReference>();
        private Post _post1;
        private Post _post2;
        private Post _post3;
        private Post _post4;
        private Post _leadPost1;
        #endregion Class Level Members

        #region How To Sample Code

        /// <summary>
        /// This method first connects to the Organization service. Afterwards, activity
        /// feeds are enabled, posts and comments are made to entity records and user
        /// walls and auto post rules are demonstrated.
        /// </summary>
        /// <param name="serverConfig">Contains server connection information.</param>
        /// <param name="promptForDelete">When True, the user will be prompted to delete all
        /// created entities.</param>
        public void Run(ServerConnection.Configuration serverConfig, bool promptForDelete)
        {
            using (_serviceProxy = ServerConnection.GetOrganizationProxy(serverConfig))
            {
                // This statement is required to enable early-bound type support.
                _serviceProxy.EnableProxyTypes();
                _serviceContext = new ServiceContext(_serviceProxy);

                try
                {
                    ConfigureActivityFeeds();
                    PostToRecordWalls();
                    PostToPersonalWalls();
                    ShowRecordWalls();
                    DeleteRequiredRecords(promptForDelete);
                }
                catch (InvalidSampleExecutionException e)
                {
                    Console.WriteLine(e.Message);
                    DeleteRequiredRecords(promptForDelete);
                }
            }
        }

        private void ConfigureActivityFeeds()
        {
            Console.WriteLine("== Configuring Activity Feeds ==");

            // Get the original systemuser config in order to keep a copy for reverting
            // after the sample has completed.
            _originalSystemUserConfig =
                (from c in _serviceContext.msdyn_PostConfigSet
                 where c.msdyn_EntityName == SystemUser.EntityLogicalName
                 select new msdyn_PostConfig
                 {
                     msdyn_PostConfigId = c.msdyn_PostConfigId,
                     msdyn_ConfigureWall = c.msdyn_ConfigureWall,
                     msdyn_EntityName = c.msdyn_EntityName
                 }).FirstOrDefault();

            // Retrieve or Create an instance of msdyn_PostConfig to enable activity
            // feeds for leads (or make sure they are already enabled).
            // If a new msdyn_PostConfig record gets created, activity feeds for
            // systemusers will be enabled automatically.
            _leadConfig =
                (from c in _serviceContext.msdyn_PostConfigSet
                 where c.msdyn_EntityName == Lead.EntityLogicalName
                 select new msdyn_PostConfig
                 {
                     msdyn_PostConfigId = c.msdyn_PostConfigId,
                     msdyn_EntityName = c.msdyn_EntityName,
                     msdyn_ConfigureWall = c.msdyn_ConfigureWall
                 }).FirstOrDefault();

            if (_leadConfig == null)
            {
                // Create the configuration record for leads.
                _leadConfig = new msdyn_PostConfig
                {
                    msdyn_EntityName = Lead.EntityLogicalName,
                    msdyn_ConfigureWall = true
                };

                _serviceContext.AddObject(_leadConfig);
                _serviceContext.SaveChanges();
                Console.WriteLine(
                    "  The lead activity feed wall configuration was created.");
            }
            else
            {
                // Store the original Lead Config so that we can revert changes later.
                _originalLeadConfig = CloneRelevantConfiguration(_leadConfig);

                if (!_leadConfig.msdyn_ConfigureWall.HasValue
                    || !_leadConfig.msdyn_ConfigureWall.Value)
                {
                    _leadConfig.msdyn_ConfigureWall = true;

                    _serviceContext.UpdateObject(_leadConfig);
                    _serviceContext.SaveChanges();
                    Console.WriteLine(
                        "  The lead activity feed wall was enabled.");
                }
            }

            // Get the original systemuser config in order to keep a copy for reverting
            // after the sample has completed.
            _systemuserConfig =
                (from c in _serviceContext.msdyn_PostConfigSet
                 where c.msdyn_EntityName == SystemUser.EntityLogicalName
                 select new msdyn_PostConfig
                 {
                     msdyn_PostConfigId = c.msdyn_PostConfigId,
                     msdyn_ConfigureWall = c.msdyn_ConfigureWall,
                     msdyn_EntityName = c.msdyn_EntityName
                 }).FirstOrDefault();

            // Ensure that the wall for systemuser is enabled if there is already a
            // systemuser configuration defined.
            if (_systemuserConfig != null &amp;&amp;
                (!_systemuserConfig.msdyn_ConfigureWall.HasValue
                || !_systemuserConfig.msdyn_ConfigureWall.Value))
            {
                _systemuserConfig.msdyn_ConfigureWall = true;

                _serviceContext.UpdateObject(_systemuserConfig);
                _serviceContext.SaveChanges();
                Console.WriteLine("  The systemuser activity feed wall was enabled.");
            }

            // Publish the lead and systemuser entities so that they will have record
            // walls on their forms.
            PublishSystemUserAndLead();

            // Activate the auto post rule configurations. New Lead qualified should be
            // activated automatically when the rule is generated by CRM.
            var leadRules =
                (from r in _serviceContext.msdyn_PostRuleConfigSet
                 where r.msdyn_RuleId == "LeadQualify.Yes.Rule"
                    || r.msdyn_RuleId == "LeadCreate.Rule"
                 select r).ToList();
            if (leadRules.Count() != 2)
            {
                throw new InvalidSampleExecutionException(
                "  One or both of the lead config rules do not exist. This can be fixed by deleting the lead post config.");
            }
            foreach (var configRule in leadRules)
            {
                _postRuleConfigs.Add(configRule);
                ActivateRuleConfig(configRule);
            }
        }

        private void PostToRecordWalls()
        {
            Console.WriteLine("\r\n== Working with Record Walls ==");
            // Create the leads.
            CreateRequiredRecords();

            // Follow each of the leads.
            _follow1 = new PostFollow
            {
                RegardingObjectId = _lead1.ToEntityReference()
            };
            _serviceContext.AddObject(_follow1);

            _follow2 = new PostFollow
            {
                RegardingObjectId = _lead2.ToEntityReference()
            };
            _serviceContext.AddObject(_follow2);

            _follow3 = new PostFollow
            {
                RegardingObjectId = _lead3.ToEntityReference()
            };
            _serviceContext.AddObject(_follow3);

            _serviceContext.SaveChanges();
            Console.WriteLine("  The 3 leads are now followed.");

            // Create posts, mentions, and comments related to the leads.
            // Create a post related to lead 1 with a mention and a comment.
            _leadPost1 = new Post
            {
                RegardingObjectId = _lead1.ToEntityReference(),
                Source = new OptionSetValue((int)PostSource.AutoPost),
                // Include a mention in the post text.
                Text = String.Format("This lead is similar to @[{0},{1},\"{2}\"]",
                    Lead.EntityTypeCode, _lead2.Id, _lead2.FullName)
            };

            _serviceContext.AddObject(_leadPost1);
            _serviceContext.SaveChanges();
            Console.WriteLine("  Post 1 has been created.");

            // It isn't necessary to keep track of the comment because the comment will
            // be deleted when its parent post is deleted.
            var comment1 = new PostComment
            {
                PostId = _leadPost1.ToEntityReference(),
                Text = "Sample comment 1"
            };
            _serviceContext.AddObject(comment1);
            _serviceContext.SaveChanges();
            Console.WriteLine("  Comment 1 has been created.");

            // Create a post related to lead 2 with three comments.
            var post2 = new Post
            {
                RegardingObjectId = _lead2.ToEntityReference(),
                Source = new OptionSetValue((int)PostSource.ManualPost),
                Text = "This lead was created for a sample."
            };

            _serviceContext.AddObject(post2);
            _serviceContext.SaveChanges();
            Console.WriteLine("  Post 2 has been created.");

            var comment2 = new PostComment
            {
                PostId = post2.ToEntityReference(),
                Text = "Sample comment 2"
            };

            var comment3 = new PostComment
            {
                PostId = post2.ToEntityReference(),
                Text = "Sample comment 3"
            };

            var comment4 = new PostComment
            {
                PostId = post2.ToEntityReference(),
                Text = "Sample comment 4"
            };

            _serviceContext.AddObject(comment2);
            _serviceContext.AddObject(comment3);
            _serviceContext.AddObject(comment4);
            _serviceContext.SaveChanges();
            Console.WriteLine("  Comments 2, 3, and 4 have been created.");

            // Qualify some leads.  Since there is an active post rule config for
            // qualification of a lead, this should generate an auto post to the record
            // wall of each lead that is qualified.

            // Qualify lead 2.
            var qualifyLead2Request = new QualifyLeadRequest
            {
                CreateAccount = true,
                LeadId = _lead2.ToEntityReference(),
                Status = new OptionSetValue((int)lead_statuscode.Qualified)
            };

            var qualifyLead2Response = (QualifyLeadResponse)_serviceProxy.Execute(
                qualifyLead2Request);

            // Store the generated Account to delete it later.
            foreach (var entityRef in qualifyLead2Response.CreatedEntities)
            {
                _generatedEntities.Add(entityRef);
            }

            Console.WriteLine("  Lead 2 was qualified.");

            // Qualify lead 3.
            var qualifyLead3Request = new QualifyLeadRequest
            {
                CreateAccount = true,
                LeadId = _lead3.ToEntityReference(),
                Status = new OptionSetValue((int)lead_statuscode.Qualified)
            };

            var qualifyLead3Response = (QualifyLeadResponse)_serviceProxy.Execute(
                qualifyLead3Request);

            foreach (var entityRef in qualifyLead3Response.CreatedEntities)
            {
                _generatedEntities.Add(entityRef);
            }

            Console.WriteLine("  Lead 3 was qualified.");
        }

        private void PostToPersonalWalls()
        {
            Console.WriteLine("\r\n== Working with Personal Walls ==");
            // Create manual (user) posts on a user's Personal wall.
            var whoAmIRequest = new WhoAmIRequest();
            var whoAmIResponse = (WhoAmIResponse)_serviceProxy.Execute(whoAmIRequest);
            var currentUserRef = new EntityReference(
                SystemUser.EntityLogicalName, whoAmIResponse.UserId);

            // Create a post that mentions lead 1.
            // The Regarding object should be set to the user whose wall the post should
            // be posted to (we'll just use the current user).
            _post1 = new Post
            {
                RegardingObjectId = currentUserRef,
                Source = new OptionSetValue((int)PostSource.ManualPost),
                Text = String.Format("I'd rather not pursue @[{0},{1},\"{2}\"]",
                    Lead.EntityTypeCode, _lead1.Id, _lead1.FullName)
            };

            _serviceContext.AddObject(_post1);
            _serviceContext.SaveChanges();
            Console.WriteLine("  Personal wall post 1 was created.");

            // Create a related comment.
            var comment1 = new PostComment
            {
                PostId = _post1.ToEntityReference(),
                Text = "Personal wall comment 1."
            };

            _serviceContext.AddObject(comment1);
            _serviceContext.SaveChanges();
            Console.WriteLine("  Personal wall comment 1 was created.");

            _post2 = new Post
            {
                RegardingObjectId = currentUserRef,
                Source = new OptionSetValue((int)PostSource.AutoPost),
                Text = "Personal wall post 2."
            };

            _serviceContext.AddObject(_post2);
            _serviceContext.SaveChanges();
            Console.WriteLine("  Personal wall post 2 was created.");

            // Create a few related comments.
            var comment2 = new PostComment
            {
                PostId = _post2.ToEntityReference(),
                Text = "Personal wall comment 2."
            };

            var comment3 = new PostComment
            {
                PostId = _post2.ToEntityReference(),
                Text = "Personal wall comment 3."
            };

            var comment4 = new PostComment
            {
                PostId = _post2.ToEntityReference(),
                Text = "Personal wall comment 4."
            };

            var comment5 = new PostComment
            {
                PostId = _post2.ToEntityReference(),
                Text = "Personal wall comment 5."
            };

            _serviceContext.AddObject(comment2);
            _serviceContext.AddObject(comment3);
            _serviceContext.AddObject(comment4);
            _serviceContext.AddObject(comment5);
            _serviceContext.SaveChanges();
            Console.WriteLine("  Personal wall comments 2, 3, 4, and 5 were created.");

            // Create a couple more posts just to show how paging works.
            _post3 = new Post
            {
                RegardingObjectId = currentUserRef,
                Source = new OptionSetValue((int)PostSource.ManualPost),
                Text = "Personal wall post 3."
            };

            _post4 = new Post
            {
                RegardingObjectId = currentUserRef,
                Source = new OptionSetValue((int)PostSource.AutoPost),
                Text = "Personal wall post 4."
            };

            _serviceContext.AddObject(_post3);
            _serviceContext.AddObject(_post4);
            _serviceContext.SaveChanges();
            Console.WriteLine("  Personal wall posts 3 and 4 were created.");

            // Retrieve this user's personal wall.
            // Retrieve the first page of posts.
            DisplayPersonalWallPage(1);

            // Retrieve the second page of posts.
            DisplayPersonalWallPage(2);

            // Sleep for a second so that the time of the newly created comment will
            // clearly be later than the previously created posts/comments (otherwise
            // Post 3 will not be escalated to the top of the wall).
            Thread.Sleep(1000);

            // Create a new comment on the last post, which will bring the post to the
            // top.
            var newPostComment = new PostComment
            {
                PostId = _post3.ToEntityReference(),
                Text = "New comment to show that new comments affect post ordering."
            };

            _serviceContext.AddObject(newPostComment);
            _serviceContext.SaveChanges();
            Console.WriteLine("\r\n  A new comment was created to show effects on post ordering.");

            // Display the first page of the personal wall to showcase the change in
            // ordering.  Post 3 should be at the top.
            DisplayPersonalWallPage(1);

            // Show paging of comments.
            // Retrieve comments 2 at a time, starting with page 1.
            var commentsQuery = new QueryExpression(PostComment.EntityLogicalName)
            {
                ColumnSet = new ColumnSet(true),
                Criteria = new FilterExpression(LogicalOperator.And),
                PageInfo = new PagingInfo
                {
                    Count = 2,
                    PageNumber = 1,
                    ReturnTotalRecordCount = true
                }
            };

            commentsQuery.Criteria.AddCondition(
                "postid", ConditionOperator.Equal, _post2.Id);

            // Continue querying for comments until there are no further comments to
            // be retrieved.
            EntityCollection commentsResult;
            do
            {
                commentsResult = _serviceProxy.RetrieveMultiple(commentsQuery);

                // Display the comments that we retrieved.
                Console.WriteLine("\r\n  Comments for lead 2 page {0}",
                    commentsQuery.PageInfo.PageNumber);
                foreach (PostComment comment in commentsResult.Entities)
                {
                    DisplayComment(comment);
                }

                commentsQuery.PageInfo.PageNumber += 1;
            }
            while (commentsResult.MoreRecords);
        }

        private void ShowRecordWalls()
        {
            Console.WriteLine("\r\n== Showing Record Walls ==");

            // Create a new post on one of the leads.
            var newLeadPost = new Post
            {
                Source = new OptionSetValue((int)PostSource.AutoPost),
                Text = "New lead post.",
                RegardingObjectId = _lead2.ToEntityReference(),
            };

            _serviceContext.AddObject(newLeadPost);
            _serviceContext.SaveChanges();
            Console.WriteLine("  The new lead 2 post has been created.");

            DisplayRecordWall(_lead1);
            DisplayRecordWall(_lead2);
            DisplayRecordWall(_lead3);
        }
        private void DisplayRecordWall(Lead lead)
        {
            // Display the first page of the record wall.
            var retrieveRecordWallReq = new RetrieveRecordWallRequest
            {
                Entity = lead.ToEntityReference(),
                CommentsPerPost = 2,
                PageSize = 10,
                PageNumber = 1
            };

            var retrieveRecordWallRes =
                (RetrieveRecordWallResponse)_serviceProxy.Execute(retrieveRecordWallReq);

            Console.WriteLine("\r\n  Posts for lead {0}:", lead.FullName);
            foreach (Post post in retrieveRecordWallRes.EntityCollection.Entities)
            {
                DisplayPost(post);
            }
        }

        private void DisplayPersonalWallPage(int pageNumber)
        {
            // Retrieve the page of posts.  We'll only retrieve 5 at a time so that
            // we will have more than one page.
            var pageSize = 5;

            var personalWallPageReq = new RetrievePersonalWallRequest
            {
                CommentsPerPost = 2,
                PageNumber = pageNumber,
                PageSize = pageSize
            };
            var personalWallPageRes =
                (RetrievePersonalWallResponse)_serviceProxy.Execute(personalWallPageReq);

            Console.WriteLine("\r\n  Personal Wall Page {0} Posts:", pageNumber);
            foreach (Post post in personalWallPageRes.EntityCollection.Entities)
            {
                DisplayPost(post);
            }
        }

        private void DisplayPost(Post post)
        {
            Console.WriteLine("    Post, {0}, {1}, {2}: {3}",
                post.CreatedBy.Name,
                (PostType)post.Type.Value,
                (PostSource)post.Source.Value,
                Shorten(post.Text));

            if (post.Post_Comments != null)
            {
                foreach (var comment in post.Post_Comments)
                {
                    DisplayComment(comment);
                }
            }
        }

        private void DisplayComment(PostComment comment)
        {
            Console.WriteLine("      Comment, {0}: {1}",
                        comment.CreatedBy.Name, Shorten(comment.Text));
        }

        private String Shorten(String x)
        {
            return x.Length > 33 ? x.Substring(0, 30) + "..." : x;
        }

        private void CreateRequiredRecords()
        {
            // Create leads for relating activity feed records to. Since there is an
            // active post rule config for creation of a new lead, creating the leads
            // should add an auto post to the record wall for each of the leads.
            _lead1 = new Lead
            {
                CompanyName = "A. Datum Corporation",
                FirstName = "Henriette",
                MiddleName = "Thaulow",
                LastName = "Andersen",
                Subject = "Activity Feeds Sample 1"
            };
            _serviceContext.AddObject(_lead1);

            _lead2 = new Lead
            {
                CompanyName = "Adventure Works",
                FirstName = "Mary",
                MiddleName = "Kay",
                LastName = "Andersen",
                Subject = "Activity Feeds Sample 2"
            };
            _serviceContext.AddObject(_lead2);

            _lead3 = new Lead
            {
                CompanyName = "Fabrikam, Inc.",
                FirstName = "Andrew",
                LastName = "Sullivan",
                Subject = "Activity Feeds Sample 3"
            };
            _serviceContext.AddObject(_lead3);

            _serviceContext.SaveChanges();

            var columnSet = new ColumnSet(true);
            _lead1 = (Lead)_serviceProxy.Retrieve(
                Lead.EntityLogicalName, _lead1.Id, columnSet);
            _lead2 = (Lead)_serviceProxy.Retrieve(
                Lead.EntityLogicalName, _lead2.Id, columnSet);
            _lead3 = (Lead)_serviceProxy.Retrieve(
                Lead.EntityLogicalName, _lead3.Id, columnSet);

            Console.WriteLine("  The leads have been created.");
        }

        private void ActivateRuleConfig(msdyn_PostRuleConfig qualifyLeadRule)
        {
            _serviceProxy.Execute(new SetStateRequest
            {
                EntityMoniker = qualifyLeadRule.ToEntityReference(),
                State = new OptionSetValue((int)msdyn_PostRuleConfigState.Active),
                Status = new OptionSetValue((int)msdyn_postruleconfig_statuscode.Active)
            });
        }

        private msdyn_PostConfig CloneRelevantConfiguration(msdyn_PostConfig config)
        {
            return new msdyn_PostConfig
            {
                msdyn_ConfigureWall = config.msdyn_ConfigureWall,
                msdyn_EntityName = config.msdyn_EntityName,
                msdyn_PostConfigId = config.msdyn_PostConfigId
            };
        }

        private void PublishSystemUserAndLead()
        {
            // The systemuser and lead entities must be published because otherwise the
            // record walls on the respective forms will not update.
            _serviceProxy.Execute(new PublishXmlRequest
            {
                ParameterXml = @"
                    <importexportxml>
                        <entities>
                            <entity>systemuser</entity>
                            <entity>lead</entity>
                        </entities>
                    </importexportxml>"
            });
            Console.WriteLine("  The systemuser and lead entities were published.");
        }

        private void DeleteRequiredRecords(bool prompt)
        {
            var toBeDeleted = true;
            if (prompt)
            {
                // Ask the user if the created entities should be deleted.
                Console.Write("\nDo you want these entity records deleted? (y/n) [y]: ");
                String answer = Console.ReadLine();
                if (answer.StartsWith("y") ||
                    answer.StartsWith("Y") ||
                    answer == String.Empty)
                {
                    toBeDeleted = true;
                }
                else
                {
                    toBeDeleted = false;
                }
            }

            if (toBeDeleted)
            {
                // Handle reverting the configurations appropriately - delete them
                // if they did not exist before.  Otherwise update them to their
                // original values.  Lead must be reverted first since it must be deleted
                // first if systemuser is to be deleted.
                RevertPostConfig(_originalLeadConfig, _leadConfig);
                RevertPostConfig(_originalSystemUserConfig, _systemuserConfig);

                // Revert the form changes.
                PublishSystemUserAndLead();

                // Delete the leads.
                DeleteFromContext(_lead1);
                DeleteFromContext(_lead2);
                DeleteFromContext(_lead3);
                var saveResult = _serviceContext.SaveChanges();
                Console.WriteLine("  The leads have been deleted.");

                Console.WriteLine("  The post follow records were deleted with the leads.");
                Console.WriteLine("  Posts that were related to leads were deleted with the leads.");

                // Delete posts that aren't regarding entities that were deleted.
                DeleteFromContext(_post1);
                DeleteFromContext(_post2);
                DeleteFromContext(_post3);
                DeleteFromContext(_post4);
                _serviceContext.SaveChanges();
                Console.WriteLine("  Posts that weren't regarding deleted entities were deleted.");

                // Delete the generated entities.
                foreach (var entityRef in _generatedEntities)
                {
                    _serviceProxy.Delete(entityRef.LogicalName, entityRef.Id);
                }
                Console.WriteLine("  All generated entities have been deleted.");
            }
        }

        private void DeleteFromContext(Entity entity)
        {
            if (!_serviceContext.IsAttached(entity))
            {
                _serviceContext.Attach(entity);
            }
            _serviceContext.DeleteObject(entity);
            _serviceContext.SaveChanges();
        }

        private void RevertPostConfig(msdyn_PostConfig originalConfig,
            msdyn_PostConfig newConfig)
        {
            if (originalConfig != null)
            {
                // Revert the rule configs.
                foreach (var rule in _postRuleConfigs.Where(
                    x => x.msdyn_PostConfigId.Id == newConfig.msdyn_PostConfigId))
                {
                    // Set the state to the original value.
                    _serviceProxy.Execute(new SetStateRequest
                    {
                        EntityMoniker = rule.ToEntityReference(),
                        State = new OptionSetValue((int)rule.statecode),
                        Status = rule.statuscode
                    });
                }

                // Update the config to the values from the original config.
                // Make sure the context is not tracking the new config and is tracking
                // the original config.
                if (!_serviceContext.IsAttached(originalConfig))
                {
                    if (_serviceContext.IsAttached(newConfig))
                    {
                        _serviceContext.Detach(newConfig);
                    }
                    _serviceContext.Attach(originalConfig);
                }
                _serviceContext.UpdateObject(originalConfig);
                _serviceContext.SaveChanges();
                Console.WriteLine(
                    "  The {0} activity feed configuration was reverted.",
                    originalConfig.msdyn_EntityName);
            }
            else
            {
                _serviceProxy.Delete(newConfig.LogicalName,
                    newConfig.Id);
                Console.WriteLine(
                    "  The {0} activity feed configuration was deleted.",
                    newConfig.msdyn_EntityName);
            }
        }

        #endregion How To Sample Code

        #region Main method

        /// <summary>
        /// Standard Main() method used by most SDK samples.
        /// </summary>
        /// <param name="args"></param>
        static public void Main(string[] args)
        {
            try
            {
                // Obtain the target organization's Web address and client logon 
                // credentials from the user.
                ServerConnection serverConnect = new ServerConnection();
                ServerConnection.Configuration config = serverConnect.GetServerConfiguration();

                var app = new WorkingWithActivityFeeds();
                app.Run(config, true);
            }
            catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> ex)
            {
                Console.WriteLine("The application terminated with an error.");
                Console.WriteLine("Timestamp: {0}", ex.Detail.Timestamp);
                Console.WriteLine("Code: {0}", ex.Detail.ErrorCode);
                Console.WriteLine("Message: {0}", ex.Detail.Message);
                Console.WriteLine("Plugin Trace: {0}", ex.Detail.TraceText);
                Console.WriteLine("Inner Fault: {0}",
                    null == ex.Detail.InnerFault ? "No Inner Fault" : "Has Inner Fault");
            }
            catch (System.TimeoutException ex)
            {
                Console.WriteLine("The application terminated with an error.");
                Console.WriteLine("Message: {0}", ex.Message);
                Console.WriteLine("Stack Trace: {0}", ex.StackTrace);
                Console.WriteLine("Inner Fault: {0}",
                    null == ex.InnerException.Message ? "No Inner Fault" : ex.InnerException.Message);
            }
            catch (System.Exception ex)
            {
                Console.WriteLine("The application terminated with an error.");
                Console.WriteLine(ex.Message);

                // Display the details of the inner exception.
                if (ex.InnerException != null)
                {
                    Console.WriteLine(ex.InnerException.Message);

                    FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> fe = ex.InnerException
                        as FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>;
                    if (fe != null)
                    {
                        Console.WriteLine("Timestamp: {0}", fe.Detail.Timestamp);
                        Console.WriteLine("Code: {0}", fe.Detail.ErrorCode);
                        Console.WriteLine("Message: {0}", fe.Detail.Message);
                        Console.WriteLine("Plugin Trace: {0}", fe.Detail.TraceText);
                        Console.WriteLine("Inner Fault: {0}",
                            null == fe.Detail.InnerFault ? "No Inner Fault" : "Has Inner Fault");
                    }
                }
            }
            // Additional exceptions to catch: SecurityTokenValidationException, ExpiredSecurityTokenException,
            // SecurityAccessDeniedException, MessageSecurityException, and SecurityNegotiationException.
            finally
            {
                Console.WriteLine("Press <Enter> to exit.");
                Console.ReadLine();
            }
        }
        #endregion Main method
    }
}

Imports System.ServiceModel
Imports System.Threading
Imports Microsoft.Crm.Sdk.Messages
Imports Microsoft.Xrm.Sdk
Imports Microsoft.Xrm.Sdk.Client
Imports Microsoft.Xrm.Sdk.Query

Namespace Microsoft.Crm.Sdk.Samples
    ''' <summary>
    ''' Demonstrates how to work with the new Activity Feeds entities, including Post,
    ''' PostFollow, and PostComment.</summary>
    ''' <remarks>
    ''' At run-time, you will be given the option to delete all the
    ''' database records created by this program.</remarks>
    Public Class WorkingWithActivityFeeds
#Region "Local Sample Types"
        Private Class InvalidSampleExecutionException
            Inherits Exception
            Public Sub New(ByVal message As String)
                MyBase.New(message)
            End Sub
        End Class
#End Region ' Local Sample Types

#Region "Class Level Members"
        Private _serviceProxy As OrganizationServiceProxy
        Private _serviceContext As ServiceContext
        Private _originalLeadConfig As msdyn_PostConfig
        Private _originalSystemUserConfig As msdyn_PostConfig
        Private _systemuserConfig As msdyn_PostConfig
        Private _leadConfig As msdyn_PostConfig
        Private _postRuleConfigs As New List(Of msdyn_PostRuleConfig)()
        Private _lead1 As Lead
        Private _lead2 As Lead
        Private _lead3 As Lead
        Private _follow1 As PostFollow
        Private _follow2 As PostFollow
        Private _follow3 As PostFollow
        Private _generatedEntities As New List(Of EntityReference)()
        Private _post1 As Post
        Private _post2 As Post
        Private _post3 As Post
        Private _post4 As Post
        Private _leadPost1 As Post
#End Region ' Class Level Members

#Region "How To Sample Code"

        ''' <summary>
        ''' This method first connects to the Organization service. Afterwards, activity
        ''' feeds are enabled, posts and comments are made to entity records and user
        ''' walls and auto post rules are demonstrated.
        ''' </summary>
        ''' <param name="serverConfig">Contains server connection information.</param>
        ''' <param name="promptForDelete">When True, the user will be prompted to delete all
        ''' created entities.</param>
        Public Sub Run(ByVal serverConfig As ServerConnection.Configuration,
                       ByVal promptForDelete As Boolean)
            _serviceProxy = ServerConnection.GetOrganizationProxy(serverConfig)
            Using _serviceProxy
                ' This statement is required to enable early-bound type support.
                _serviceProxy.EnableProxyTypes()
                _serviceContext = New ServiceContext(_serviceProxy)

                Try
                    ConfigureActivityFeeds()
                    PostToRecordWalls()
                    PostToPersonalWalls()
                    ShowRecordWalls()
                    DeleteRequiredRecords(promptForDelete)
                Catch e As InvalidSampleExecutionException
                    Console.WriteLine(e.Message)
                    DeleteRequiredRecords(promptForDelete)
                End Try
            End Using
        End Sub

        Private Sub ConfigureActivityFeeds()
            Console.WriteLine("== Configuring Activity Feeds ==")

            ' Get the original systemuser config in order to keep a copy for reverting
            ' after the sample has completed.
            _originalSystemUserConfig = ( _
                From c In _serviceContext.msdyn_PostConfigSet _
                Where c.msdyn_EntityName Is SystemUser.EntityLogicalName _
                Select New msdyn_PostConfig With
                       {
                           .msdyn_PostConfigId = c.msdyn_PostConfigId,
                           .msdyn_ConfigureWall = c.msdyn_ConfigureWall,
            .msdyn_EntityName = c.msdyn_EntityName
                       }).FirstOrDefault()

            ' Retrieve or Create an instance of msdyn_PostConfig to enable activity
            ' feeds for leads (or make sure they are already enabled).
            ' If a new msdyn_PostConfig record gets created, activity feeds for
            ' systemusers will be enabled automatically.
            _leadConfig = ( _
                From c In _serviceContext.msdyn_PostConfigSet _
                Where c.msdyn_EntityName Is Lead.EntityLogicalName _
                Select New msdyn_PostConfig With
                       {
                           .msdyn_PostConfigId = c.msdyn_PostConfigId,
                           .msdyn_EntityName = c.msdyn_EntityName,
                           .msdyn_ConfigureWall = c.msdyn_ConfigureWall
                       }).FirstOrDefault()

            If _leadConfig Is Nothing Then
                ' Create the configuration record for leads.
                _leadConfig = New msdyn_PostConfig With
                              {
                                  .msdyn_EntityName = Lead.EntityLogicalName,
                                  .msdyn_ConfigureWall = True
                              }

                _serviceContext.AddObject(_leadConfig)
                _serviceContext.SaveChanges()
                Console.WriteLine("  The lead activity feed wall configuration was created.")
            Else
                ' Store the original Lead Config so that we can revert changes later.
                _originalLeadConfig = CloneRevelantConfiguration(_leadConfig)

                If (Not _leadConfig.msdyn_ConfigureWall.HasValue) _
                    OrElse (Not _leadConfig.msdyn_ConfigureWall.Value) Then
                    _leadConfig.msdyn_ConfigureWall = True

                    _serviceContext.UpdateObject(_leadConfig)
                    _serviceContext.SaveChanges()
                    Console.WriteLine("  The lead activity feed wall was enabled.")
                End If
            End If

            ' Get the original systemuser config in order to keep a copy for reverting
            ' after the sample has completed.
            _systemuserConfig = ( _
                From c In _serviceContext.msdyn_PostConfigSet _
                Where c.msdyn_EntityName Is SystemUser.EntityLogicalName _
                Select New msdyn_PostConfig With
                       {
                           .msdyn_PostConfigId = c.msdyn_PostConfigId,
                           .msdyn_ConfigureWall = c.msdyn_ConfigureWall,
                           .msdyn_EntityName = c.msdyn_EntityName
                       }).FirstOrDefault()

            ' Ensure that the wall for systemuser is enabled if there is already a
            ' systemuser configuration defined.
            If _systemuserConfig IsNot Nothing AndAlso
                ((Not _systemuserConfig.msdyn_ConfigureWall.HasValue) OrElse
                 (Not _systemuserConfig.msdyn_ConfigureWall.Value)) Then
                _systemuserConfig.msdyn_ConfigureWall = True

                _serviceContext.UpdateObject(_systemuserConfig)
                _serviceContext.SaveChanges()
                Console.WriteLine("  The systemuser activity feed wall was enabled.")
            End If

            ' Publish the lead and systemuser entities so that they will have record
            ' walls on their forms.
            PublishSystemUserAndLead()

            ' Activate the auto post rule configurations. New Lead qualified should be
            ' activated automatically when the rule is generated by CRM.
            Dim leadRules = ( _
                From r In _serviceContext.msdyn_PostRuleConfigSet _
                Where r.msdyn_RuleId Is "LeadQualify.Yes.Rule" OrElse
                r.msdyn_RuleId Is "LeadCreate.Rule" _
                Select r).ToList()
            If leadRules.Count() <> 2 Then
                Throw New InvalidSampleExecutionException( _
                    "  One or both of the lead config rules do not exist." _
                    &amp; "This can be fixed by deleting the lead post config.")
            End If
            For Each configRule In leadRules
                _postRuleConfigs.Add(configRule)
                ActivateRuleConfig(configRule)
            Next configRule
        End Sub

        Private Sub PostToRecordWalls()
            Console.WriteLine(vbCrLf &amp; "== Working with Record Walls ==")
            ' Create the leads.
            CreateRequiredRecords()

            ' Follow each of the leads.
            _follow1 = New PostFollow With
                       {
                           .RegardingObjectId = _lead1.ToEntityReference()
                       }
            _serviceContext.AddObject(_follow1)

            _follow2 = New PostFollow With
                       {
                           .RegardingObjectId = _lead2.ToEntityReference()
                       }
            _serviceContext.AddObject(_follow2)

            _follow3 = New PostFollow With
                       {
                           .RegardingObjectId = _lead3.ToEntityReference()
                       }
            _serviceContext.AddObject(_follow3)

            _serviceContext.SaveChanges()
            Console.WriteLine("  The 3 leads are now followed.")

            ' Create posts, mentions, and comments related to the leads.
            ' Create a post related to lead 1 with a mention and a comment.
            _leadPost1 = New Post With
                         {
                             .RegardingObjectId = _lead1.ToEntityReference(),
                             .Source = New OptionSetValue(CInt(Fix(PostSource.AutoPost))),
                             .Text = String.Format("This lead is similar to @[{0},{1},""{2}""]",
                                                   Lead.EntityTypeCode,
                                                   _lead2.Id,
                                                   _lead2.FullName)
                         }
            ' Include a mention in the post text.

            _serviceContext.AddObject(_leadPost1)
            _serviceContext.SaveChanges()
            Console.WriteLine("  Post 1 has been created.")

            ' It isn't necessary to keep track of the comment because the comment will
            ' be deleted when its parent post is deleted.
            Dim comment1 = New PostComment With
                           {
                               .PostId = _leadPost1.ToEntityReference(),
                               .Text = "Sample comment 1"
                           }
            _serviceContext.AddObject(comment1)
            _serviceContext.SaveChanges()
            Console.WriteLine("  Comment 1 has been created.")

            ' Create a post related to lead 2 with three comments.
            Dim post2 = New Post With
                        {
                            .RegardingObjectId = _lead2.ToEntityReference(),
                            .Source = New OptionSetValue(CInt(Fix(PostSource.ManualPost))),
                            .Text = "This lead was created for a sample."
                        }

            _serviceContext.AddObject(post2)
            _serviceContext.SaveChanges()
            Console.WriteLine("  Post 2 has been created.")

            Dim comment2 = New PostComment With
                           {
                               .PostId = post2.ToEntityReference(),
                               .Text = "Sample comment 2"
                           }

            Dim comment3 = New PostComment With
                           {
                               .PostId = post2.ToEntityReference(),
                               .Text = "Sample comment 3"
                           }

            Dim comment4 = New PostComment With
                           {
                               .PostId = post2.ToEntityReference(),
                               .Text = "Sample comment 4"
                           }

            _serviceContext.AddObject(comment2)
            _serviceContext.AddObject(comment3)
            _serviceContext.AddObject(comment4)
            _serviceContext.SaveChanges()
            Console.WriteLine("  Comments 2, 3, and 4 have been created.")

            ' Qualify some leads.  Since there is an active post rule config for
            ' qualification of a lead, this should generate an auto post to the record
            ' wall of each lead that is qualified.

            ' Qualify lead 2.
            Dim qualifyLead2Request = New QualifyLeadRequest With
                                      {
                                          .CreateAccount = True,
                                          .LeadId = _lead2.ToEntityReference(),
                                          .Status =
                                          New OptionSetValue(
                                              CInt(Fix(lead_statuscode.Qualified)))
                                      }

            Dim qualifyLead2Response = CType(_serviceProxy.Execute(qualifyLead2Request), 
                QualifyLeadResponse)

            ' Store the generated Account to delete it later.
            For Each entityRef In qualifyLead2Response.CreatedEntities
                _generatedEntities.Add(entityRef)
            Next entityRef

            Console.WriteLine("  Lead 2 was qualified.")

            ' Qualify lead 3.
            Dim qualifyLead3Request = New QualifyLeadRequest With
                                      {
                                          .CreateAccount = True,
                                          .LeadId = _lead3.ToEntityReference(),
                                          .Status = New OptionSetValue(
                                              CInt(Fix(lead_statuscode.Qualified)))
                                      }

            Dim qualifyLead3Response = CType(_serviceProxy.Execute(qualifyLead3Request), 
                QualifyLeadResponse)

            For Each entityRef In qualifyLead3Response.CreatedEntities
                _generatedEntities.Add(entityRef)
            Next entityRef

            Console.WriteLine("  Lead 3 was qualified.")
        End Sub

        Private Sub PostToPersonalWalls()
            Console.WriteLine(vbCrLf &amp; "== Working with Personal Walls ==")
            ' Create manual (user) posts on a user's Personal wall.
            Dim whoAmIRequest = New WhoAmIRequest()
            Dim whoAmIResponse = CType(_serviceProxy.Execute(whoAmIRequest), 
                WhoAmIResponse)
            Dim currentUserRef = New EntityReference(SystemUser.EntityLogicalName,
                                                     whoAmIResponse.UserId)

            ' Create a post that mentions lead 1.
            ' The Regarding object should be set to the user whose wall the post should
            ' be posted to (we'll just use the current user).
            _post1 = New Post With
                     {
                         .RegardingObjectId = currentUserRef,
                         .Source = New OptionSetValue(CInt(Fix(PostSource.ManualPost))),
                         .Text = String.Format("I'd rather not pursue @[{0},{1},""{2}""]",
                                               Lead.EntityTypeCode,
                                               _lead1.Id,
                                               _lead1.FullName)
                     }

            _serviceContext.AddObject(_post1)
            _serviceContext.SaveChanges()
            Console.WriteLine("  Personal wall post 1 was created.")

            ' Create a related comment.
            Dim comment1 = New PostComment With
                           {
                               .PostId = _post1.ToEntityReference(),
                               .Text = "Personal wall comment 1."
                           }

            _serviceContext.AddObject(comment1)
            _serviceContext.SaveChanges()
            Console.WriteLine("  Personal wall comment 1 was created.")

            _post2 = New Post With
                     {
                         .RegardingObjectId = currentUserRef,
                         .Source = New OptionSetValue(CInt(Fix(PostSource.AutoPost))),
                         .Text = "Personal wall post 2."
                     }

            _serviceContext.AddObject(_post2)
            _serviceContext.SaveChanges()
            Console.WriteLine("  Personal wall post 2 was created.")

            ' Create a few related comments.
            Dim comment2 = New PostComment With
                           {
                               .PostId = _post2.ToEntityReference(),
                               .Text = "Personal wall comment 2."
                           }

            Dim comment3 = New PostComment With
                           {
                               .PostId = _post2.ToEntityReference(),
                               .Text = "Personal wall comment 3."
                           }

            Dim comment4 = New PostComment With
                           {
                               .PostId = _post2.ToEntityReference(),
                               .Text = "Personal wall comment 4."
                           }

            Dim comment5 = New PostComment With
                           {
                               .PostId = _post2.ToEntityReference(),
                               .Text = "Personal wall comment 5."
                           }

            _serviceContext.AddObject(comment2)
            _serviceContext.AddObject(comment3)
            _serviceContext.AddObject(comment4)
            _serviceContext.AddObject(comment5)
            _serviceContext.SaveChanges()
            Console.WriteLine("  Personal wall comments 2, 3, 4, and 5 were created.")

            ' Create a couple more posts just to show how paging works.
            _post3 = New Post With
                     {
                         .RegardingObjectId = currentUserRef,
                         .Source = New OptionSetValue(
                             CInt(Fix(PostSource.ManualPost))),
                         .Text = "Personal wall post 3."
                     }

            _post4 = New Post With
                     {
                         .RegardingObjectId = currentUserRef,
                         .Source = New OptionSetValue(
                             CInt(Fix(PostSource.AutoPost))),
                         .Text = "Personal wall post 4."
                     }

            _serviceContext.AddObject(_post3)
            _serviceContext.AddObject(_post4)
            _serviceContext.SaveChanges()
            Console.WriteLine("  Personal wall posts 3 and 4 were created.")

            ' Retrieve this user's personal wall.
            ' Retrieve the first page of posts.
            DisplayPersonalWallPage(1)

            ' Retrieve the second page of posts.
            DisplayPersonalWallPage(2)

            ' Sleep for a second so that the time of the newly created comment will
            ' clearly be later than the previously created posts/comments (otherwise
            ' Post 3 will not be escalated to the top of the wall).
            Thread.Sleep(1000)

            ' Create a new comment on the last post, which will bring the post to the
            ' top.
            Dim newPostComment =
                New PostComment With
                {
                    .PostId = _post3.ToEntityReference(),
                    .Text = "New comment to show that new comments affect post ordering."
                }

            _serviceContext.AddObject(newPostComment)
            _serviceContext.SaveChanges()
            Console.WriteLine(
                vbCrLf _
                &amp; "  A new comment was created to show effects on post ordering.")

            ' Display the first page of the personal wall to showcase the change in
            ' ordering.  Post 3 should be at the top.
            DisplayPersonalWallPage(1)

            ' Show paging of comments.
            ' Retrieve comments 2 at a time, starting with page 1.
            Dim commentsQuery = New QueryExpression(PostComment.EntityLogicalName) With
                                {
                                    .ColumnSet = New ColumnSet(True),
                                    .Criteria = New FilterExpression(LogicalOperator.And),
                                    .PageInfo = New PagingInfo With
                                                {
                                                    .Count = 2,
                                                    .PageNumber = 1,
                                                    .ReturnTotalRecordCount = True
                                                }
                                }

            commentsQuery.Criteria.AddCondition("postid",
                                                ConditionOperator.Equal,
                                                _post2.Id)

            ' Continue querying for comments until there are no further comments to
            ' be retrieved.
            Dim commentsResult As EntityCollection
            Do
                commentsResult = _serviceProxy.RetrieveMultiple(commentsQuery)

                ' Display the comments that we retrieved.
                Console.WriteLine(vbCrLf &amp; "  Comments for lead 2 page {0}",
                                  commentsQuery.PageInfo.PageNumber)
                For Each comment As PostComment In commentsResult.Entities
                    DisplayComment(comment)
                Next comment

                commentsQuery.PageInfo.PageNumber += 1
            Loop While commentsResult.MoreRecords
        End Sub

        Private Sub ShowRecordWalls()
            Console.WriteLine(vbCrLf &amp; "== Showing Record Walls ==")

            ' Create a new post on one of the leads.
            Dim newLeadPost = New Post With
                              {
                                  .Source = New OptionSetValue(
                                      CInt(Fix(PostSource.AutoPost))),
                                  .Text = "New lead post.",
                                  .RegardingObjectId = _lead2.ToEntityReference()
                              }

            _serviceContext.AddObject(newLeadPost)
            _serviceContext.SaveChanges()
            Console.WriteLine("  The new lead 2 post has been created.")

            DisplayRecordWall(_lead1)
            DisplayRecordWall(_lead2)
            DisplayRecordWall(_lead3)
        End Sub

        Private Sub DisplayRecordWall(ByVal lead As Lead)
            ' Display the first page of the record wall.
            Dim retrieveRecordWallReq = New RetrieveRecordWallRequest With
                                        {
                                            .Entity = lead.ToEntityReference(),
                                            .CommentsPerPost = 2,
                                            .PageSize = 10,
                                            .PageNumber = 1
                                        }

            Dim retrieveRecordWallRes = CType(_serviceProxy.Execute(retrieveRecordWallReq), 
                RetrieveRecordWallResponse)

            Console.WriteLine(vbCrLf &amp; "  Posts for lead {0}:", lead.FullName)
            For Each post As Post In retrieveRecordWallRes.EntityCollection.Entities
                DisplayPost(post)
            Next post
        End Sub


        Private Sub DisplayPersonalWallPage(ByVal pageNumber As Integer)
            ' Retrieve the page of posts.  We'll only retrieve 5 at a time so that
            ' we will have more than one page.
            Dim pageSize = 5

            Dim personalWallPageReq = New RetrievePersonalWallRequest With
                                      {
                                          .CommentsPerPost = 2,
                                          .PageNumber = pageNumber,
                                          .PageSize = pageSize
                                      }
            Dim personalWallPageRes = CType(_serviceProxy.Execute(personalWallPageReq), 
                RetrievePersonalWallResponse)

            Console.WriteLine(vbCrLf &amp; "  Personal Wall Page {0} Posts:", pageNumber)
            For Each post As Post In personalWallPageRes.EntityCollection.Entities
                DisplayPost(post)
            Next post
        End Sub


        Private Sub DisplayPost(ByVal post As Post)
            Console.WriteLine("    Post, {0}, {1}, {2}: {3}",
                              post.CreatedBy.Name,
                              CType(post.Type.Value, PostType),
                              CType(post.Source.Value, PostSource),
                              Shorten(post.Text))

            If post.Post_Comments IsNot Nothing Then
                For Each comment In post.Post_Comments
                    DisplayComment(comment)
                Next comment
            End If
        End Sub

        Private Sub DisplayComment(ByVal comment As PostComment)
            Console.WriteLine("      Comment, {0}: {1}",
                              comment.CreatedBy.Name,
                              Shorten(comment.Text))
        End Sub

        Private Function Shorten(ByVal x As String) As String
            Return If(x.Length > 33, x.Substring(0, 30) &amp; "...", x)
        End Function

        Private Sub CreateRequiredRecords()
            ' Create leads for relating activity feed records to. Since there is an
            ' active post rule config for creation of a new lead, creating the leads
            ' should add an auto post to the record wall for each of the leads.
            _lead1 = New Lead With
                     {
                         .CompanyName = "A. Datum Corporation",
                         .FirstName = "Henriette",
                         .MiddleName = "Thaulow",
                         .LastName = "Andersen",
                         .Subject = "Activity Feeds Sample 1"
                     }
            _serviceContext.AddObject(_lead1)

            _lead2 = New Lead With
                     {
                         .CompanyName = "Adventure Works",
                         .FirstName = "Mary",
                         .MiddleName = "Kay",
                         .LastName = "Andersen",
                         .Subject = "Activity Feeds Sample 2"
                     }
            _serviceContext.AddObject(_lead2)

            _lead3 = New Lead With
                     {
                         .CompanyName = "Fabrikam, Inc.",
                         .FirstName = "Andrew",
                         .LastName = "Sullivan",
                         .Subject = "Activity Feeds Sample 3"
                     }
            _serviceContext.AddObject(_lead3)

            _serviceContext.SaveChanges()

            Dim columnSet = New ColumnSet(True)
            _lead1 = CType(_serviceProxy.Retrieve(Lead.EntityLogicalName,
                                                  _lead1.Id, columnSet), 
                                              Lead)
            _lead2 = CType(_serviceProxy.Retrieve(Lead.EntityLogicalName,
                                                  _lead2.Id, columnSet), 
                                              Lead)
            _lead3 = CType(_serviceProxy.Retrieve(Lead.EntityLogicalName,
                                                  _lead3.Id, columnSet), 
                                              Lead)

            Console.WriteLine("  The leads have been created.")
        End Sub

        Private Sub ActivateRuleConfig(ByVal qualifyLeadRule As msdyn_PostRuleConfig)
            _serviceProxy.Execute(New SetStateRequest With
                                  {
                                      .EntityMoniker = qualifyLeadRule.ToEntityReference(),
                                      .State = New OptionSetValue(
                                          CInt(Fix(msdyn_PostRuleConfigState.Active))),
                                      .Status = New OptionSetValue(
                                          CInt(Fix(msdyn_postruleconfig_statuscode.Active)))
                                  })
        End Sub

        Private Function CloneRevelantConfiguration(ByVal config As msdyn_PostConfig) As msdyn_PostConfig
            Return New msdyn_PostConfig With
                   {
                       .msdyn_ConfigureWall = config.msdyn_ConfigureWall,
                       .msdyn_EntityName = config.msdyn_EntityName,
                       .msdyn_PostConfigId = config.msdyn_PostConfigId
                   }
        End Function

        Private Sub PublishSystemUserAndLead()
            ' The systemuser and lead entities must be published because otherwise the
            ' record walls on the respective forms will not update.
            _serviceProxy.Execute(New PublishXmlRequest With
                                  {
                                      .ParameterXml = "" _
                                   &amp; "                    <importexportxml>" _
                                   &amp; "                        <entities>" _
                                   &amp; "                            <entity>systemuser</entity>" _
                                   &amp; "                            <entity>lead</entity>" _
                                   &amp; "                        </entities>" _
                                   &amp; "                    </importexportxml>"
                                  })
            Console.WriteLine("  The systemuser and lead entities were published.")
        End Sub

        Private Sub DeleteRequiredRecords(ByVal prompt As Boolean)
            Dim toBeDeleted = True
            If prompt Then
                ' Ask the user if the created entities should be deleted.
                Console.Write(vbLf &amp; "Do you want these entity records deleted? (y/n) [y]: ")
                Dim answer As String = Console.ReadLine()
                If answer.StartsWith("y") OrElse
                    answer.StartsWith("Y") OrElse
                    answer = String.Empty Then
                    toBeDeleted = True
                Else
                    toBeDeleted = False
                End If
            End If

            If toBeDeleted Then
                ' Handle reverting the configurations appropriately - delete them
                ' if they did not exist before.  Otherwise update them to their
                ' original values.  Lead must be reverted first since it must be deleted
                ' first if systemuser is to be deleted.
                RevertPostConfig(_originalLeadConfig, _leadConfig)
                RevertPostConfig(_originalSystemUserConfig, _systemuserConfig)

                ' Revert the form changes.
                PublishSystemUserAndLead()

                ' Delete the leads.
                DeleteFromContext(_lead1)
                DeleteFromContext(_lead2)
                DeleteFromContext(_lead3)
                Dim saveResult = _serviceContext.SaveChanges()
                Console.WriteLine("  The leads have been deleted.")
                Console.WriteLine("  The post follow records were deleted with the leads.")
                Console.WriteLine("  Posts that were related to leads were deleted with the leads.")

                ' Delete posts that aren't regarding entities that were deleted.
                DeleteFromContext(_post1)
                DeleteFromContext(_post2)
                DeleteFromContext(_post3)
                DeleteFromContext(_post4)
                _serviceContext.SaveChanges()
                Console.WriteLine("  Posts that weren't regarding deleted entities were deleted.")

                ' Delete the generated entities.
                For Each entityRef In _generatedEntities
                    _serviceProxy.Delete(entityRef.LogicalName, entityRef.Id)
                Next entityRef
                Console.WriteLine("  All generated entities have been deleted.")
            End If
        End Sub

        Private Sub DeleteFromContext(ByVal entity As Entity)
            If Not _serviceContext.IsAttached(entity) Then
                _serviceContext.Attach(entity)
            End If
            _serviceContext.DeleteObject(entity)
            _serviceContext.SaveChanges()
        End Sub

        Private Sub RevertPostConfig(ByVal originalConfig As msdyn_PostConfig,
                                     ByVal newConfig As msdyn_PostConfig)
            If originalConfig IsNot Nothing Then
                ' Revert the rule configs.
                For Each rule In _postRuleConfigs.Where(
                    Function(x) x.msdyn_PostConfigId.Id = newConfig.msdyn_PostConfigId.Value)
                    ' Set the state to the original value.
                    _serviceProxy.Execute(New SetStateRequest With
                                          {
                                              .EntityMoniker = rule.ToEntityReference(),
                                              .State = New OptionSetValue(
                                                  CInt(Fix(rule.statecode))),
                                              .Status = rule.statuscode
                                          })
                Next rule

                ' Update the config to the values from the original config.
                ' Make sure the context is not tracking the new config and is tracking
                ' the original config.
                If Not _serviceContext.IsAttached(originalConfig) Then
                    If _serviceContext.IsAttached(newConfig) Then
                        _serviceContext.Detach(newConfig)
                    End If
                    _serviceContext.Attach(originalConfig)
                End If
                _serviceContext.UpdateObject(originalConfig)
                _serviceContext.SaveChanges()
                Console.WriteLine("  The {0} activity feed configuration was reverted.",
                                  originalConfig.msdyn_EntityName)
            Else
                _serviceProxy.Delete(newConfig.LogicalName, newConfig.Id)
                Console.WriteLine("  The {0} activity feed configuration was deleted.",
                                  newConfig.msdyn_EntityName)
            End If
        End Sub

#End Region ' How To Sample Code

#Region "Main method"

        ''' <summary>
        ''' Standard Main() method used by most SDK samples.
        ''' </summary>
        ''' <param name="args"></param>
        Public Shared Sub Main(ByVal args() As String)
            Try
                ' Obtain the target organization's Web address and client logon 
                ' credentials from the user.
                Dim serverConnect As New ServerConnection()
                Dim config As ServerConnection.Configuration =
                    serverConnect.GetServerConfiguration()

                Dim app = New WorkingWithActivityFeeds()
                app.Run(config, True)
            Catch ex As FaultException(Of Microsoft.Xrm.Sdk.OrganizationServiceFault)
                Console.WriteLine("The application terminated with an error.")
                Console.WriteLine("Timestamp: {0}", ex.Detail.Timestamp)
                Console.WriteLine("Code: {0}", ex.Detail.ErrorCode)
                Console.WriteLine("Message: {0}", ex.Detail.Message)
                Console.WriteLine("Plugin Trace: {0}", ex.Detail.TraceText)
                Console.WriteLine("Inner Fault: {0}",
                                  If(Nothing Is ex.Detail.InnerFault, "No Inner Fault", "Has Inner Fault"))
            Catch ex As TimeoutException
                Console.WriteLine("The application terminated with an error.")
                Console.WriteLine("Message: {0}", ex.Message)
                Console.WriteLine("Stack Trace: {0}", ex.StackTrace)
                Console.WriteLine("Inner Fault: {0}",
                                  If(Nothing Is ex.InnerException.Message, "No Inner Fault", ex.InnerException.Message))
            Catch ex As Exception
                Console.WriteLine("The application terminated with an error.")
                Console.WriteLine(ex.Message)

                ' Display the details of the inner exception.
                If ex.InnerException IsNot Nothing Then
                    Console.WriteLine(ex.InnerException.Message)

                    Dim fe As FaultException(Of Microsoft.Xrm.Sdk.OrganizationServiceFault) =
                        TryCast(ex.InnerException, 
                            FaultException(Of Microsoft.Xrm.Sdk.OrganizationServiceFault))
                    If fe IsNot Nothing Then
                        Console.WriteLine("Timestamp: {0}", fe.Detail.Timestamp)
                        Console.WriteLine("Code: {0}", fe.Detail.ErrorCode)
                        Console.WriteLine("Message: {0}", fe.Detail.Message)
                        Console.WriteLine("Plugin Trace: {0}", fe.Detail.TraceText)
                        Console.WriteLine("Inner Fault: {0}",
                                          If(Nothing Is fe.Detail.InnerFault, "No Inner Fault", "Has Inner Fault"))
                    End If
                End If
                ' Additional exceptions to catch: SecurityTokenValidationException, ExpiredSecurityTokenException,
                ' SecurityAccessDeniedException, MessageSecurityException, and SecurityNegotiationException.
            Finally
                Console.WriteLine("Press <Enter> to exit.")
                Console.ReadLine()
            End Try
        End Sub
#End Region ' Main method
    End Class
End Namespace

See Also

RetrievePersonalWallRequest
RetrieveRecordWallRequest
Activity feeds entities
Introduction to activity feeds
Configure activity feeds
Post entity messages and methods
PostComment (comment) entity messages and methods
PostFollow (follow) entity messages and methods