Ejemplo n.º 1
0
        internal ConceptualRevision(
            int conceptualEntityId, IEnumerable <Tuple <IEnumerable <RevisionId>, IEnumerable <Revision> > > entityTypeAndRevisionSetPairs,
            Func <Func <IEnumerable <RevisionId>, IEnumerable <int> >, ConceptualEntityStateType> conceptualEntityStateSelector,
            Func <Func <IEnumerable <RevisionId>, IEnumerable <RevisionIdDelta <UserType> > >, ConceptualEntityDeltaType> conceptualEntityDeltaSelector,
            UserTransaction transaction, UserType user, ConceptualRevision <ConceptualEntityStateType, ConceptualEntityDeltaType, UserType> previous)
        {
            this.conceptualEntityId = conceptualEntityId;

            var cachedEntityTypeAndRevisionSetPairs =
                new Lazy <IReadOnlyCollection <Tuple <IEnumerable <RevisionId>, IEnumerable <Revision> > > >(() => entityTypeAndRevisionSetPairs.ToImmutableArray());

            revisionDictionariesByEntityType =
                new Lazy <ImmutableDictionary <IEnumerable <RevisionId>, ImmutableDictionary <int, Tuple <Revision, UserTransaction, UserType> > > >(
                    () => {
                if (previous == null)
                {
                    return(cachedEntityTypeAndRevisionSetPairs.Value.ToImmutableDictionary(
                               entityTypeAndRevisions => entityTypeAndRevisions.Item1,
                               entityTypeAndRevisions => entityTypeAndRevisions.Item2.ToImmutableDictionary(i => i.LatestRevisionId, i => Tuple.Create(i, transaction, user))));
                }

                var newEntityTypeAndRevisionDictionaryPairs =
                    new List <KeyValuePair <IEnumerable <RevisionId>, ImmutableDictionary <int, Tuple <Revision, UserTransaction, UserType> > > >(
                        cachedEntityTypeAndRevisionSetPairs.Value.Count);
                foreach (var entityTypeAndRevisions in cachedEntityTypeAndRevisionSetPairs.Value)
                {
                    var revisionsByLatestRevisionId = previous.revisionDictionariesByEntityType.Value.GetValueOrDefault(
                        entityTypeAndRevisions.Item1,
                        ImmutableDictionary <int, Tuple <Revision, UserTransaction, UserType> > .Empty);
                    newEntityTypeAndRevisionDictionaryPairs.Add(
                        new KeyValuePair <IEnumerable <RevisionId>, ImmutableDictionary <int, Tuple <Revision, UserTransaction, UserType> > >(
                            entityTypeAndRevisions.Item1,
                            revisionsByLatestRevisionId.SetItems(
                                entityTypeAndRevisions.Item2.Select(
                                    i => new KeyValuePair <int, Tuple <Revision, UserTransaction, UserType> >(i.LatestRevisionId, Tuple.Create(i, transaction, user))))));
                }
                return(previous.revisionDictionariesByEntityType.Value.SetItems(newEntityTypeAndRevisionDictionaryPairs));
            });

            conceptualEntityState =
                new Lazy <ConceptualEntityStateType>(
                    () =>
                    conceptualEntityStateSelector(
                        entityType =>
                        revisionDictionariesByEntityType.Value.GetValueOrDefault(entityType, ImmutableDictionary <int, Tuple <Revision, UserTransaction, UserType> > .Empty)
                        .Values.Select(i => i.Item1.RevisionId)));

            conceptualEntityDelta = new Lazy <ConceptualEntityDeltaType>(
                () => {
                var revisionSetsByEntityType = cachedEntityTypeAndRevisionSetPairs.Value.ToImmutableDictionary(i => i.Item1, i => i.Item2);
                return(conceptualEntityDeltaSelector(
                           entityType => revisionSetsByEntityType.GetValueOrDefault(entityType, new Revision[0]).Select(
                               revision => {
                    Tuple <Revision, UserTransaction, UserType> previousRevisionAndTransactionAndUser = null;
                    if (previous != null)
                    {
                        var previousRevisionsByLatestRevisionId = previous.revisionDictionariesByEntityType.Value.GetValueOrDefault(entityType);
                        if (previousRevisionsByLatestRevisionId != null)
                        {
                            previousRevisionAndTransactionAndUser = previousRevisionsByLatestRevisionId.GetValueOrDefault(revision.LatestRevisionId);
                        }
                    }
                    return previousRevisionAndTransactionAndUser == null
                                                                               ? new RevisionIdDelta <UserType>(revision.RevisionId, null, null, default(UserType))
                                                                               : new RevisionIdDelta <UserType>(
                        revision.RevisionId,
                        previousRevisionAndTransactionAndUser.Item1.RevisionId,
                        previousRevisionAndTransactionAndUser.Item2,
                        previousRevisionAndTransactionAndUser.Item3);
                })));
            });

            this.transaction = transaction;
            this.user        = user;
            this.previous    = previous;
        }
        /// <summary>
        /// Returns a list of the revisions that are related to the specified revision IDs. This includes those that match the IDs as well as all others that share
        /// a latest revision ID. The revisions within a user transaction that have the same "conceptual entity" ID are grouped into a single "conceptual revision".
        /// The list is ordered by transaction date/time, descending.
        /// </summary>
        /// <param name="entityTypeRevisionIdLists">The revision-ID lists. Use a separate list for each database entity type that you would like to aggregate into
        /// the conceptual revisions.</param>
        /// <param name="conceptualEntityStateSelector">A function that uses a revision-ID lookup function to return a representation of the conceptual entity's
        /// state at a particular revision. The lookup function takes one of the revision-ID-list references from the first parameter and returns the set of
        /// revision IDs for that entity type that were effective at the revision. You can use this to create a conceptual-entity-specific object, maybe with an
        /// anonymous class. If it's convenient, you may also return null if there is no data at the revision. Do not pass null.</param>
        /// <param name="conceptualEntityDeltaSelector">A function that uses a revision-ID lookup function to return a representation of the conceptual entity's
        /// changes in a particular revision. The lookup function takes one of the revision-ID-list references from the first parameter and returns a set of
        /// revision-ID-delta objects for that entity type. You can use this to create a conceptual-entity-specific object, maybe with an anonymous class. If it's
        /// convenient, you may also return null if there is no data for the revision. Do not pass null.</param>
        /// <param name="userSelector">A function that takes a user ID and returns the corresponding user object. Do not pass null.</param>
        public static IEnumerable <ConceptualRevision <ConceptualEntityStateType, ConceptualEntityDeltaType, UserType> > GetAllRelatedRevisions
        <ConceptualEntityStateType, ConceptualEntityDeltaType, UserType>(
            IEnumerable <IEnumerable <RevisionId> > entityTypeRevisionIdLists,
            Func <Func <IEnumerable <RevisionId>, IEnumerable <int> >, ConceptualEntityStateType> conceptualEntityStateSelector,
            Func <Func <IEnumerable <RevisionId>, IEnumerable <RevisionIdDelta <UserType> > >, ConceptualEntityDeltaType> conceptualEntityDeltaSelector,
            Func <int, UserType> userSelector)
        {
            var revisionsById = RevisionsById;
            var entityIdsAndRevisionIdListsByLatestRevisionId = entityTypeRevisionIdLists.SelectMany(
                i => i,
                (list, revisionId) => {
                var revision = revisionsById[revisionId.Id];
                return(new { revision.LatestRevisionId, ConceptualEntityId = revisionId.ConceptualEntityId ?? revision.LatestRevisionId, RevisionIdList = list });
            }).GroupBy(i => i.LatestRevisionId).ToDictionary(
                i => i.Key,
                grouping => {
                var cachedGrouping = grouping.ToArray();
                return(Tuple.Create(
                           new HashSet <int>(cachedGrouping.Select(i => i.ConceptualEntityId)),
                           new HashSet <IEnumerable <RevisionId> >(cachedGrouping.Select(i => i.RevisionIdList))));
            });

            // Pre-filter user transactions to avoid having to sort the full list below.
            var revisionsByLatestRevisionId = RevisionsByLatestRevisionId;
            var userTransactionsById        = UserTransactionsById;
            var userTransactions            =
                entityIdsAndRevisionIdListsByLatestRevisionId.Keys.SelectMany(i => revisionsByLatestRevisionId[i])
                .Select(i => userTransactionsById[i.UserTransactionId])
                .Distinct();

            var revisionsByUserTransactionId    = RevisionsByUserTransactionId;
            var entityIdAndRevisionIdListGetter = new Func <int, Tuple <HashSet <int>, HashSet <IEnumerable <RevisionId> > > >(
                latestRevisionId => {
                Tuple <HashSet <int>, HashSet <IEnumerable <RevisionId> > > val;
                entityIdsAndRevisionIdListsByLatestRevisionId.TryGetValue(latestRevisionId, out val);
                return(val);
            });
            var entityTransactions = from transaction in from i in userTransactions orderby i.TransactionDateTime, i.UserTransactionId select i
            let user = transaction.UserId.HasValue ? userSelector(transaction.UserId.Value) : default(UserType)
                       from entityGrouping in from revision in revisionsByUserTransactionId[transaction.UserTransactionId]
                       let entityIdsAndRevisionIdLists = entityIdAndRevisionIdListGetter(revision.LatestRevisionId)
                                                         where entityIdsAndRevisionIdLists != null
                                                         from entityId in entityIdsAndRevisionIdLists.Item1
                                                         from revisionIdList in entityIdsAndRevisionIdLists.Item2
                                                         group new { revisionIdList, revision } by entityId
            into grouping
            orderby grouping.Key
            select grouping
            let entityId = entityGrouping.Key
                           let revisionIdListAndRevisionSetPairs =
                from i in entityGrouping
                group i.revision by i.revisionIdList
                into grouping select Tuple.Create(grouping.Key, grouping.AsEnumerable())
                select new { entityId, revisionIdListAndRevisionSetPairs, transaction, user };

            var conceptualRevisions = new List <ConceptualRevision <ConceptualEntityStateType, ConceptualEntityDeltaType, UserType> >();
            var lastConceptualRevisionsByEntityId = new Dictionary <int, ConceptualRevision <ConceptualEntityStateType, ConceptualEntityDeltaType, UserType> >();

            foreach (var entityTransaction in entityTransactions)
            {
                ConceptualRevision <ConceptualEntityStateType, ConceptualEntityDeltaType, UserType> lastConceptualRevision;
                lastConceptualRevisionsByEntityId.TryGetValue(entityTransaction.entityId, out lastConceptualRevision);

                var newConceptualRevision = new ConceptualRevision <ConceptualEntityStateType, ConceptualEntityDeltaType, UserType>(
                    entityTransaction.entityId,
                    entityTransaction.revisionIdListAndRevisionSetPairs,
                    conceptualEntityStateSelector,
                    conceptualEntityDeltaSelector,
                    entityTransaction.transaction,
                    entityTransaction.user,
                    lastConceptualRevision);
                conceptualRevisions.Add(newConceptualRevision);
                lastConceptualRevisionsByEntityId[entityTransaction.entityId] = newConceptualRevision;
            }

            return(conceptualRevisions.AsEnumerable().Reverse());
        }