public MessageRecord(MessageMemento message, FactID ancestorFact, RoleMemento ancestorRole, Guid source)
 {
     _message = message;
     _ancestorFact = ancestorFact;
     _ancestorRole = ancestorRole;
     _source = source;
 }
        public FactMemento Load(string domain, FactID factId)
        {
            IdentifiedFactMemento identifiedMemento = null;

            using (var session = new Session(_connectionString))
            {
                // Get the fact.
                session.Command.CommandText = HEAD_SELECT +
                                              "WHERE f.FactID = @FactID " +
                                              TAIL_JOIN +
                                              "ORDER BY p.PredecessorID";
                AddParameter(session.Command, "@FactID", factId.key);
                using (IDataReader reader = session.Command.ExecuteReader())
                {
                    session.Command.Parameters.Clear();
                    identifiedMemento = LoadMementosFromReader(reader).FirstOrDefault();
                    if (identifiedMemento == null)
                    {
                        throw new CorrespondenceException(string.Format("Unable to find fact {0}", factId.key));
                    }
                }
            }

            return(identifiedMemento.Memento);
        }
        public void SaveWindowsPhoneSubscriptionIsIdempotent()
        {
            Guid   gameGuid = Guid.NewGuid();
            var    game     = NewGame(gameGuid);
            FactID gameId   = _repository.Save(TestDomain, game, TestClient);

            List <FactID> pivotIds = new List <FactID> {
                gameId
            };

            _repository.SaveWindowsPhoneSubscription(
                pivotIds,
                "windows_phone_device_uri",
                TestClient);
            _repository.SaveWindowsPhoneSubscription(
                pivotIds,
                "windows_phone_device_uri",
                TestClient);
            List <WindowsPhoneSubscription> subscriptions = _repository.LoadWindowsPhoneSubscriptions(
                pivotIds, AnotherClient);

            Assert.AreEqual(1, subscriptions.Count);
            Assert.AreEqual(gameId, subscriptions[0].PivotFactId);
            Assert.AreEqual("windows_phone_device_uri", subscriptions[0].DeviceUri);
        }
 public MessageRecord(MessageMemento message, FactID ancestorFact, RoleMemento ancestorRole, Guid source)
 {
     _message      = message;
     _ancestorFact = ancestorFact;
     _ancestorRole = ancestorRole;
     _source       = source;
 }
        private static FactMemento NewMove(FactID gameId, byte moveIndex)
        {
            var move = new FactMemento(MoveType);

            move.Data = new byte[] { moveIndex };
            move.AddPredecessor(MoveGame, gameId, true);
            return(move);
        }
 public List <FactID> LoadRecentMessages(string domain, FactID pivotId, Guid clientGuid, TimestampID timestamp)
 {
     using (var procedures = new Procedures(new Session(_connectionString)))
     {
         int clientId = SaveClient(procedures, clientGuid);
         return(procedures.GetRecentMessages(pivotId, timestamp, clientId));
     }
 }
 public void DeleteMessage(FactID ancestorFactId, int ancestorRoleId)
 {
     _session.Command.CommandText = "DELETE FROM Message " +
                                    "WHERE AncestorFactId = @AncestorFactId AND AncestorRoleId = @AncestorRoleId";
     AddParameter("@AncestorFactId", ancestorFactId.key);
     AddParameter("@AncestorRoleId", ancestorRoleId);
     _session.Command.ExecuteNonQuery();
     _session.Command.Parameters.Clear();
 }
 public void DeleteMessage(FactID ancestorFactId, int ancestorRoleId)
 {
     _session.Command.CommandText = "DELETE FROM Message " +
         "WHERE AncestorFactId = @AncestorFactId AND AncestorRoleId = @AncestorRoleId";
     AddParameter("@AncestorFactId", ancestorFactId.key);
     AddParameter("@AncestorRoleId", ancestorRoleId);
     _session.Command.ExecuteNonQuery();
     _session.Command.Parameters.Clear();
 }
 public void InsertPredecessor(FactID id, PredecessorMemento predecessor, int roleId)
 {
     _session.Command.CommandText = "INSERT Predecessor (FKFactID, FKRoleID, FKPredecessorFactID, IsPivot) VALUES (@FactID, @RoleID, @PredecessorFactID, @IsPivot)";
     AddParameter("@FactID", id.key);
     AddParameter("@RoleID", roleId);
     AddParameter("@PredecessorFactID", predecessor.ID.key);
     AddParameter("@IsPivot", predecessor.IsPivot);
     _session.Command.ExecuteNonQuery();
     _session.Command.Parameters.Clear();
 }
        public void CanLoadAFact()
        {
            Guid   gameGuid = Guid.NewGuid();
            var    game     = NewGame(gameGuid);
            FactID gameId   = _repository.Save(TestDomain, game, TestClient);

            var loadedGame = _repository.Load(TestDomain, gameId);

            Assert.AreEqual(game, loadedGame);
        }
 private void AddToFactTree(string domain, FactTreeMemento messageBody, FactID factId, Dictionary <FactID, FactID> localIdByRemoteId)
 {
     if (!messageBody.Contains(factId))
     {
         FactMemento fact = _repository.Load(domain, factId);
         foreach (PredecessorMemento predecessor in fact.Predecessors)
         {
             AddToFactTree(domain, messageBody, predecessor.ID, localIdByRemoteId);
         }
         messageBody.Add(new IdentifiedFactMemento(factId, fact));
     }
 }
 public FactMemento Load(string domain, FactID factId)
 {
     lock (this)
     {
         FactRecord factRecord = _factTable.FirstOrDefault(o =>
             o.IdentifiedFactMemento.Id.Equals(factId));
         if (factRecord != null)
             return factRecord.IdentifiedFactMemento.Memento;
         else
             throw new CorrespondenceException(
                 string.Format("Fact with id {0} not found.", factId));
     }
 }
        public void CanSaveAndFindAFact()
        {
            Guid gameGuid = Guid.NewGuid();

            var    saveGame = NewGame(gameGuid);
            FactID saveId   = _repository.Save(TestDomain, saveGame, TestClient);

            var    findGame = NewGame(gameGuid);
            FactID?findId   = _repository.FindExistingFact(TestDomain, findGame);

            Assert.IsNotNull(findId);
            Assert.AreEqual(saveId, findId);
        }
        public FactMemento Load(string domain, FactID factId)
        {
            IdentifiedFactMemento identifiedMemento = null;

            using (var procedures = new Procedures(new Session(_connectionString)))
            {
                identifiedMemento = procedures.GetIdentifiedMemento(factId);
            }
            if (identifiedMemento == null)
                throw new CorrespondenceException(string.Format("Unable to find fact {0}", factId.key));

            return identifiedMemento.Memento;
        }
        public void SkipMessagesOriginatingFromThisClient()
        {
            Guid   gameGuid = Guid.NewGuid();
            var    game     = NewGame(gameGuid);
            FactID gameId   = _repository.Save(TestDomain, game, TestClient);

            var    saveMove = NewMove(gameId, 1);
            FactID saveId   = _repository.Save(TestDomain, saveMove, TestClient);

            List <FactID> messages = _repository.LoadRecentMessages(
                TestDomain, gameId, TestClient, new TimestampID(0, 0));

            Assert.AreEqual(0, messages.Count);
        }
 public IdentifiedFactMemento GetIdentifiedMemento(FactID factId)
 {
     // Get the fact.
     _session.Command.CommandText = HEAD_SELECT +
                                    "WHERE f.FactID = @FactID " +
                                    TAIL_JOIN +
                                    "ORDER BY p.PredecessorID";
     AddParameter("@FactID", factId.key);
     using (var loader = new Loader(_session.Command.ExecuteReader()))
     {
         _session.Command.Parameters.Clear();
         return(loader.LoadMementos().FirstOrDefault());
     }
 }
        public void FactIsPublishedToPredecessor()
        {
            Guid   gameGuid = Guid.NewGuid();
            var    game     = NewGame(gameGuid);
            FactID gameId   = _repository.Save(TestDomain, game, TestClient);

            var    saveMove = NewMove(gameId, 1);
            FactID saveId   = _repository.Save(TestDomain, saveMove, TestClient);

            List <FactID> messages = _repository.LoadRecentMessages(
                TestDomain, gameId, AnotherClient, new TimestampID(0, 0));

            Assert.AreEqual(1, messages.Count);
            Assert.AreEqual(saveId, messages[0]);
        }
        public FactMemento Load(string domain, FactID factId)
        {
            IdentifiedFactMemento identifiedMemento = null;

            using (var procedures = new Procedures(new Session(_connectionString)))
            {
                identifiedMemento = procedures.GetIdentifiedMemento(factId);
            }
            if (identifiedMemento == null)
            {
                throw new CorrespondenceException(string.Format("Unable to find fact {0}", factId.key));
            }

            return(identifiedMemento.Memento);
        }
        public void CanSaveAndFindAFactWithPredecessor()
        {
            Guid   gameGuid = Guid.NewGuid();
            var    game     = NewGame(gameGuid);
            FactID gameId   = _repository.Save(TestDomain, game, TestClient);

            var    saveMove = NewMove(gameId, 1);
            FactID saveId   = _repository.Save(TestDomain, saveMove, TestClient);

            var    findMove = NewMove(gameId, 1);
            FactID?findId   = _repository.FindExistingFact(TestDomain, findMove);

            Assert.IsNotNull(findId);
            Assert.AreEqual(saveId, findId);
        }
        private void Repository_PivotAffected(string domain, FactID pivotId, FactID factId, Guid clientGuid)
        {
            _messageBus.Notify(domain, pivotId);

            var subscribers = _repository.LoadWindowsPhoneSubscriptions(new List <FactID> {
                pivotId
            }, clientGuid);

            if (subscribers.Any())
            {
                var messageBody = new FactTreeMemento(0);
                AddToFactTree(domain, messageBody, factId, null);
                SendWindowsPhonePushNotifications(subscribers, messageBody);
            }
        }
 public void Notify(string domain, FactID pivotId)
 {
     List<Registration> registrations;
     lock (this)
     {
         registrations = _registrations
             .Where(n =>
                 n.Domain == domain &&
                 n.PivotId.Equals(pivotId))
             .ToList();
     }
     foreach (var registration in registrations)
     {
         registration.Notify();
     }
 }
示例#22
0
 public FactMemento Load(string domain, FactID factId)
 {
     lock (this)
     {
         FactRecord factRecord = _factTable.FirstOrDefault(o =>
                                                           o.IdentifiedFactMemento.Id.Equals(factId));
         if (factRecord != null)
         {
             return(factRecord.IdentifiedFactMemento.Memento);
         }
         else
         {
             throw new CorrespondenceException(
                       string.Format("Fact with id {0} not found.", factId));
         }
     }
 }
        public void Notify(string domain, FactID pivotId)
        {
            List <Registration> registrations;

            lock (this)
            {
                registrations = _registrations
                                .Where(n =>
                                       n.Domain == domain &&
                                       n.PivotId.Equals(pivotId))
                                .ToList();
            }
            foreach (var registration in registrations)
            {
                registration.Notify();
            }
        }
        public void Notify(string domain, FactID pivotId)
        {
            List <Registration> registrations;

            lock (this)
            {
                Debug.WriteLine(String.Format("Notify all {0} of {1}", pivotId, _registrations.Count));
                registrations = _registrations
                                .Where(n =>
                                       n.Domain == domain &&
                                       n.PivotId.Equals(pivotId))
                                .ToList();
            }
            foreach (var registration in registrations)
            {
                Debug.WriteLine(String.Format("Notify {0} {1}", registration.ClientGuid, registration.PivotId));
                registration.Cancellation.Cancel();
            }
        }
        private bool FindExistingFact(FactMemento memento, out FactID id, Session session, bool readCommitted)
        {
            var existingFacts = FindExistingFacts(memento, session, readCommitted);

            if (existingFacts.Count > 1)
            {
                throw new CorrespondenceException(string.Format("More than one fact matched the given {0}.", memento.FactType));
            }
            if (existingFacts.Count == 1)
            {
                id = existingFacts[0].Id;
                return(true);
            }
            else
            {
                id = new FactID();
                return(false);
            }
        }
        public List <FactID> GetRecentMessages(FactID pivotId, TimestampID timestamp, int clientId)
        {
            _session.Command.CommandText =
                "SELECT TOP (20) FactId " +
                "FROM Message " +
                "WHERE PivotId = @PivotId " +
                "AND FactId > @Timestamp " +
                "AND ClientId != @ClientId " +
                "ORDER BY FactId";
            AddParameter("@PivotId", pivotId.key);
            AddParameter("@Timestamp", timestamp.Key);
            AddParameter("@ClientId", clientId);
            using (var loader = new Loader(_session.Command.ExecuteReader()))
            {
                _session.Command.Parameters.Clear();

                return(loader.LoadIDs().ToList());
            }
        }
        public void CanUnpublishAFact()
        {
            Guid   gameGuid = Guid.NewGuid();
            var    game     = NewGame(gameGuid);
            FactID gameId   = _repository.Save(TestDomain, game, TestClient);

            var    move   = NewMove(gameId, 1);
            FactID moveId = _repository.Save(TestDomain, move, TestClient);

            _repository.DeleteMessages(TestDomain, new List <UnpublishMemento>
            {
                new UnpublishMemento(moveId, MoveGame)
            });

            List <FactID> messages = _repository.LoadRecentMessages(
                TestDomain, gameId, AnotherClient, new TimestampID(0, 0));

            Assert.AreEqual(0, messages.Count);
        }
        public void DontReturnSubscriptionsFromYourself()
        {
            Guid   gameGuid = Guid.NewGuid();
            var    game     = NewGame(gameGuid);
            FactID gameId   = _repository.Save(TestDomain, game, TestClient);

            List <FactID> pivotIds = new List <FactID> {
                gameId
            };

            _repository.SaveWindowsPhoneSubscription(
                pivotIds,
                "windows_phone_device_uri",
                TestClient);
            List <WindowsPhoneSubscription> subscriptions = _repository.LoadWindowsPhoneSubscriptions(
                pivotIds, TestClient);

            Assert.AreEqual(0, subscriptions.Count);
        }
        public List <FactID> LoadRecentMessages(string domain, FactID pivotId, Guid clientGuid, TimestampID timestamp)
        {
            using (var session = new Session(_connectionString))
            {
                int clientId = SaveClient(session, clientGuid);
                session.Command.CommandText =
                    "SELECT TOP (20) FactId " +
                    "FROM Message " +
                    "WHERE PivotId = @PivotId " +
                    "AND FactId > @Timestamp " +
                    "AND ClientId != @ClientId " +
                    "ORDER BY FactId";
                AddParameter(session.Command, "@PivotId", pivotId.key);
                AddParameter(session.Command, "@Timestamp", timestamp.Key);
                AddParameter(session.Command, "@ClientId", clientId);
                using (IDataReader messageReader = session.Command.ExecuteReader())
                {
                    session.Command.Parameters.Clear();

                    return(LoadIDsFromReader(messageReader).ToList());
                }
            }
        }
 private bool FindExistingFact(FactMemento memento, out FactID id, Procedures procedures, bool readCommitted)
 {
     var existingFacts = FindExistingFacts(memento, procedures, readCommitted);
     if (existingFacts.Count > 1)
         throw new CorrespondenceException(string.Format(
             "More than one fact matched the given {0}.",
             memento.FactType));
     if (existingFacts.Count == 1)
     {
         id = existingFacts[0].Id;
         return true;
     }
     else
     {
         id = new FactID();
         return false;
     }
 }
示例#31
0
 private void Repository_PivotAffected(string domain, FactID pivotId)
 {
     _messageBus.Notify(domain, pivotId);
 }
 public List<FactID> LoadRecentMessages(string domain, FactID pivotId, Guid clientGuid, TimestampID timestamp)
 {
     using (var procedures = new Procedures(new Session(_connectionString)))
     {
         int clientId = SaveClient(procedures, clientGuid);
         return procedures.GetRecentMessages(pivotId, timestamp, clientId);
     }
 }
        public FactID Save(string domain, FactMemento fact, Guid clientGuid)
        {
            // Retry on concurrency failure.
            while (true)
            {
                using (var procedures = new Procedures(new Session(_connectionString)))
                {
                    procedures.BeginTransaction();

                    // First see if the fact is already in storage.
                    FactID id;
                    if (FindExistingFact(fact, out id, procedures, readCommitted: true))
                    {
                        return(id);
                    }

                    // It isn't there, so store it.
                    int typeId = SaveType(procedures, fact.FactType);
                    id = procedures.InsertFact(fact, typeId);

                    // Store the predecessors.
                    foreach (PredecessorMemento predecessor in fact.Predecessors)
                    {
                        int roleId = SaveRole(procedures, predecessor.Role);
                        procedures.InsertPredecessor(id, predecessor, roleId);
                    }

                    // Store a message for each pivot.
                    FactID newFactId = id;
                    List <AncestorMessage> pivotMessages = fact.Predecessors
                                                           .Where(predecessor => predecessor.IsPivot)
                                                           .Select(predecessor =>
                                                                   new AncestorMessage
                    {
                        AncestorFactId = newFactId,
                        AncestorRoleId = SaveRole(procedures, predecessor.Role),
                        Message        = new MessageMemento(predecessor.ID, newFactId)
                    })
                                                           .ToList();

                    // Store messages for each non-pivot. This fact belongs to all predecessors' pivots.
                    string[] nonPivots = fact.Predecessors
                                         .Where(predecessor => !predecessor.IsPivot)
                                         .Select(predecessor => predecessor.ID.key.ToString())
                                         .ToArray();
                    List <AncestorMessage> nonPivotMessages;
                    if (nonPivots.Length > 0)
                    {
                        var predecessorsPivotRoles = procedures.GetAncestorPivots(nonPivots);
                        nonPivotMessages = predecessorsPivotRoles
                                           .Select(predecessorPivot =>
                                                   new AncestorMessage
                        {
                            AncestorFactId = predecessorPivot.AncestorFactId,
                            AncestorRoleId = predecessorPivot.AncestorRoleId,
                            Message        = new MessageMemento(predecessorPivot.PivotId, newFactId)
                        })
                                           .ToList();
                    }
                    else
                    {
                        nonPivotMessages = new List <AncestorMessage>();
                    }

                    int clientId     = SaveClient(procedures, clientGuid);
                    var roleMessages = pivotMessages.Union(nonPivotMessages).Distinct().ToList();
                    procedures.InsertMessages(roleMessages, clientId);

                    // Optimistic concurrency check.
                    // Make sure we don't find more than one.
                    var existingFacts = FindExistingFacts(fact, procedures, readCommitted: false);
                    if (existingFacts.Count == 1)
                    {
                        procedures.Commit();

                        if (roleMessages.Any() && PivotAffected != null)
                        {
                            foreach (var roleMessage in roleMessages)
                            {
                                PivotAffected(domain, roleMessage.Message.PivotId, roleMessage.Message.FactId, clientGuid);
                            }
                        }
                        return(id);
                    }
                    else
                    {
                        _retried = true;
                    }
                }
            }
        }
示例#34
0
 private void Unpublish(FactID factId, RoleMemento role)
 {
     _messageTable.RemoveAll(message =>
                             message.AncestorFact.Equals(factId) &&
                             message.AncestorRole.Equals(role));
 }
示例#35
0
        public FactID Save(string domain, FactMemento fact, Guid clientGuid)
        {
            FactID        factId;
            List <FactID> affectedPivots;

            lock (this)
            {
                // See if the fact already exists.
                FactRecord existingFact = _factTable.FirstOrDefault(o =>
                                                                    o.IdentifiedFactMemento.Memento.Equals(fact));
                if (existingFact == null)
                {
                    // It doesn't, so create it.
                    FactID newFactID = new FactID()
                    {
                        key = _factTable.Count + 1
                    };
                    factId       = newFactID;
                    existingFact = new FactRecord()
                    {
                        IdentifiedFactMemento = new IdentifiedFactMemento(factId, fact)
                    };

                    _factTable.Add(existingFact);

                    // Store a message for each pivot.
                    var pivots = fact.Predecessors
                                 .Where(predecessor => predecessor.IsPivot);
                    _messageTable.AddRange(pivots
                                           .Select(predecessor => new MessageRecord(
                                                       new MessageMemento(predecessor.ID, newFactID),
                                                       newFactID,
                                                       predecessor.Role,
                                                       clientGuid)));

                    // Store messages for each non-pivot. This fact belongs to all predecessors' pivots.
                    List <FactID> nonPivots = fact.Predecessors
                                              .Where(predecessor => !predecessor.IsPivot)
                                              .Select(predecessor => predecessor.ID)
                                              .ToList();
                    List <MessageRecord> predecessorsPivots = _messageTable
                                                              .Where(message => nonPivots.Contains(message.Message.FactId))
                                                              .Distinct()
                                                              .ToList();
                    _messageTable.AddRange(predecessorsPivots
                                           .Select(predecessorPivot => new MessageRecord(
                                                       new MessageMemento(predecessorPivot.Message.PivotId, newFactID),
                                                       predecessorPivot.AncestorFact,
                                                       predecessorPivot.AncestorRole,
                                                       clientGuid)));

                    affectedPivots =
                        pivots
                        .Select(pivot => pivot.ID)
                        .Union(predecessorsPivots
                               .Select(predecessorPivot => predecessorPivot.Message.PivotId))
                        .ToList();
                }
                else
                {
                    factId         = existingFact.IdentifiedFactMemento.Id;
                    affectedPivots = null;
                }
            }

            if (affectedPivots != null && affectedPivots.Any() && PivotAffected != null)
            {
                foreach (var pivotId in affectedPivots)
                {
                    PivotAffected(domain, pivotId, factId, clientGuid);
                }
            }

            return(factId);
        }
示例#36
0
 public List <FactID> LoadRecentMessages(string domain, FactID localPivotId, Guid clientGuid, TimestampID timestamp)
 {
     return(_messageTable.Where(message => message.Message.PivotId.Equals(localPivotId) && message.Message.FactId.key > timestamp.Key && message.Source != clientGuid).Select(message => message.Message.FactId).Distinct()
            .ToList());
 }
        private void Repository_PivotAffected(string domain, FactID pivotId, FactID factId, Guid clientGuid)
        {
            _messageBus.Notify(domain, pivotId);

            var subscribers = _repository.LoadWindowsPhoneSubscriptions(new List<FactID> { pivotId }, clientGuid);
            if (subscribers.Any())
            {
                var messageBody = new FactTreeMemento(0);
                AddToFactTree(domain, messageBody, factId, null);
                SendWindowsPhonePushNotifications(subscribers, messageBody);
            }
        }
 private void AddToFactTree(string domain, FactTreeMemento messageBody, FactID factId, Dictionary<FactID, FactID> localIdByRemoteId)
 {
     if (!messageBody.Contains(factId))
     {
         FactMemento fact = _repository.Load(domain, factId);
         foreach (PredecessorMemento predecessor in fact.Predecessors)
             AddToFactTree(domain, messageBody, predecessor.ID, localIdByRemoteId);
         messageBody.Add(new IdentifiedFactMemento(factId, fact));
     }
 }
 public IdentifiedFactMemento GetIdentifiedMemento(FactID factId)
 {
     // Get the fact.
     _session.Command.CommandText = HEAD_SELECT +
         "WHERE f.FactID = @FactID " +
         TAIL_JOIN +
         "ORDER BY p.PredecessorID";
     AddParameter("@FactID", factId.key);
     using (var loader = new Loader(_session.Command.ExecuteReader()))
     {
         _session.Command.Parameters.Clear();
         return loader.LoadMementos().FirstOrDefault();
     }
 }
        public FactID Save(string domain, FactMemento fact, Guid clientGuid)
        {
            // Retry on concurrency failure.
            while (true)
            {
                using (var session = new Session(_connectionString))
                {
                    session.BeginTransaction();

                    // First see if the fact is already in storage.
                    FactID id;
                    if (FindExistingFact(fact, out id, session, readCommitted: true))
                    {
                        return(id);
                    }

                    // It isn't there, so store it.
                    int typeId = SaveType(session, fact.FactType);
                    session.Command.CommandText = "INSERT Fact (FKTypeID, Data, Hashcode) VALUES (@TypeID, @Data, @Hashcode)";
                    AddParameter(session.Command, "@TypeID", typeId);
                    AddParameter(session.Command, "@Data", fact.Data);
                    AddParameter(session.Command, "@Hashcode", fact.GetHashCode());
                    session.Command.ExecuteNonQuery();
                    session.Command.Parameters.Clear();

                    session.Command.CommandText = "SELECT @@IDENTITY";
                    decimal result = (decimal)session.Command.ExecuteScalar();
                    session.Command.Parameters.Clear();
                    id.key = (Int64)result;

                    // Store the predecessors.
                    foreach (PredecessorMemento predecessor in fact.Predecessors)
                    {
                        int roleId = SaveRole(session, predecessor.Role);
                        session.Command.CommandText = "INSERT Predecessor (FKFactID, FKRoleID, FKPredecessorFactID, IsPivot) VALUES (@FactID, @RoleID, @PredecessorFactID, @IsPivot)";
                        AddParameter(session.Command, "@FactID", id.key);
                        AddParameter(session.Command, "@RoleID", roleId);
                        AddParameter(session.Command, "@PredecessorFactID", predecessor.ID.key);
                        AddParameter(session.Command, "@IsPivot", predecessor.IsPivot);
                        session.Command.ExecuteNonQuery();
                        session.Command.Parameters.Clear();
                    }

                    // Store a message for each pivot.
                    FactID newFactId = id;
                    List <MessageMemento> pivotMessages = fact.Predecessors
                                                          .Where(predecessor => predecessor.IsPivot)
                                                          .Select(predecessor => new MessageMemento(predecessor.ID, newFactId))
                                                          .ToList();

                    // Store messages for each non-pivot. This fact belongs to all predecessors' pivots.
                    string[] nonPivots = fact.Predecessors
                                         .Where(predecessor => !predecessor.IsPivot)
                                         .Select(predecessor => predecessor.ID.key.ToString())
                                         .ToArray();
                    List <MessageMemento> nonPivotMessages;
                    if (nonPivots.Length > 0)
                    {
                        string nonPivotGroup = string.Join(",", nonPivots);
                        session.Command.CommandText = string.Format(
                            "SELECT DISTINCT PivotId FROM Message WHERE FactId IN ({0})",
                            nonPivotGroup);
                        List <FactID> predecessorsPivots;
                        using (IDataReader predecessorPivotReader = session.Command.ExecuteReader())
                        {
                            session.Command.Parameters.Clear();
                            predecessorsPivots = LoadIDsFromReader(predecessorPivotReader).ToList();
                        }

                        nonPivotMessages = predecessorsPivots
                                           .Select(predecessorPivot => new MessageMemento(predecessorPivot, newFactId))
                                           .ToList();
                    }
                    else
                    {
                        nonPivotMessages = new List <MessageMemento>();
                    }

                    int clientId = SaveClient(session, clientGuid);
                    var messages = pivotMessages.Union(nonPivotMessages).Distinct().ToList();
                    SaveMessages(session, messages, clientId);

                    // Optimistic concurrency check.
                    // Make sure we don't find more than one.
                    var existingFacts = FindExistingFacts(fact, session, readCommitted: false);
                    if (existingFacts.Count == 1)
                    {
                        session.Commit();

                        if (messages.Any() && PivotAffected != null)
                        {
                            foreach (var message in messages)
                            {
                                PivotAffected(domain, message.PivotId);
                            }
                        }
                        return(id);
                    }
                    else
                    {
                        _retried = true;
                    }
                }
            }
        }
 private static FactMemento NewMove(FactID gameId, byte moveIndex)
 {
     var move = new FactMemento(MoveType);
     move.Data = new byte[] { moveIndex };
     move.AddPredecessor(MoveGame, gameId, true);
     return move;
 }
        public List<FactID> GetRecentMessages(FactID pivotId, TimestampID timestamp, int clientId)
        {
            _session.Command.CommandText =
                "SELECT TOP (20) FactId " +
                "FROM Message " +
                "WHERE PivotId = @PivotId " +
                "AND FactId > @Timestamp " +
                "AND ClientId != @ClientId " +
                "ORDER BY FactId";
            AddParameter("@PivotId", pivotId.key);
            AddParameter("@Timestamp", timestamp.Key);
            AddParameter("@ClientId", clientId);
            using (var loader = new Loader(_session.Command.ExecuteReader()))
            {
                _session.Command.Parameters.Clear();

                return loader.LoadIDs().ToList();
            }
        }
        public FactID Save(string domain, FactMemento fact, Guid clientGuid)
        {
            FactID factId;
            List<FactID> affectedPivots;

            lock (this)
            {
                // See if the fact already exists.
                FactRecord existingFact = _factTable.FirstOrDefault(o =>
                    o.IdentifiedFactMemento.Memento.Equals(fact));
                if (existingFact == null)
                {
                    // It doesn't, so create it.
                    FactID newFactID = new FactID() { key = _factTable.Count + 1 };
                    factId = newFactID;
                    existingFact = new FactRecord()
                    {
                        IdentifiedFactMemento = new IdentifiedFactMemento(factId, fact)
                    };

                    _factTable.Add(existingFact);

                    // Store a message for each pivot.
                    var pivots = fact.Predecessors
                        .Where(predecessor => predecessor.IsPivot);
                    _messageTable.AddRange(pivots
                        .Select(predecessor => new MessageRecord(
                            new MessageMemento(predecessor.ID, newFactID),
                            newFactID,
                            predecessor.Role,
                            clientGuid)));

                    // Store messages for each non-pivot. This fact belongs to all predecessors' pivots.
                    List<FactID> nonPivots = fact.Predecessors
                        .Where(predecessor => !predecessor.IsPivot)
                        .Select(predecessor => predecessor.ID)
                        .ToList();
                    List<MessageRecord> predecessorsPivots = _messageTable
                        .Where(message => nonPivots.Contains(message.Message.FactId))
                        .Distinct()
                        .ToList();
                    _messageTable.AddRange(predecessorsPivots
                        .Select(predecessorPivot => new MessageRecord(
                            new MessageMemento(predecessorPivot.Message.PivotId, newFactID),
                            predecessorPivot.AncestorFact,
                            predecessorPivot.AncestorRole,
                            clientGuid)));

                    affectedPivots =
                        pivots
                            .Select(pivot => pivot.ID)
                        .Union(predecessorsPivots
                            .Select(predecessorPivot => predecessorPivot.Message.PivotId))
                        .ToList();
                }
                else
                {
                    factId = existingFact.IdentifiedFactMemento.Id;
                    affectedPivots = null;
                }
            }

            if (affectedPivots != null && affectedPivots.Any() && PivotAffected != null)
                foreach (var pivotId in affectedPivots)
                    PivotAffected(domain, pivotId, factId, clientGuid);

            return factId;
        }
 public List<FactID> LoadRecentMessages(string domain, FactID localPivotId, Guid clientGuid, TimestampID timestamp)
 {
     return _messageTable.Where(message => message.Message.PivotId.Equals(localPivotId) && message.Message.FactId.key > timestamp.Key && message.Source != clientGuid).Select(message => message.Message.FactId).Distinct()
         .ToList();
 }
 public void InsertPredecessor(FactID id, PredecessorMemento predecessor, int roleId)
 {
     _session.Command.CommandText = "INSERT Predecessor (FKFactID, FKRoleID, FKPredecessorFactID, IsPivot) VALUES (@FactID, @RoleID, @PredecessorFactID, @IsPivot)";
     AddParameter("@FactID", id.key);
     AddParameter("@RoleID", roleId);
     AddParameter("@PredecessorFactID", predecessor.ID.key);
     AddParameter("@IsPivot", predecessor.IsPivot);
     _session.Command.ExecuteNonQuery();
     _session.Command.Parameters.Clear();
 }