public void Init()
        {
            EventStoreDbContext.NameOrConnectionString =
                @"Data Source=(localdb)\v11.0; Integrated Security=True; MultipleActiveResultSets=False; Initial Catalog=ItsCqrsTestsEventStore";

            using (var eventStore = new EventStoreDbContext())
            {
                new EventStoreDatabaseInitializer<EventStoreDbContext>().InitializeDatabase(eventStore);
            }
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ExclusiveEventStoreCatchupQuery" /> class.
        /// </summary>
        /// <param name="dbContext">The event store database context to execute the query against.</param>
        /// <param name="lockResourceName">Name of the lock. Multiple instances compete with other instances having the same <paramref name="lockResourceName" />.</param>
        /// <param name="getStartAtId">The id of the first event to query.</param>
        /// <param name="applyFilter">Transforms a query to filter the events to be read from the event store.</param>
        /// <param name="batchSize">The number of events queried from the event store at each iteration.</param>
        public ExclusiveEventStoreCatchupQuery(
            EventStoreDbContext dbContext,
            string lockResourceName,
            Func <long> getStartAtId,
            Func <IQueryable <StorableEvent>, IQueryable <StorableEvent> > applyFilter,
            int batchSize = 10000)
        {
            if (batchSize < 1)
            {
                throw new ArgumentException($"{nameof(batchSize)} must be greater than zero.");
            }

            BatchSize             = batchSize;
            this.dbContext        = dbContext;
            this.lockResourceName = lockResourceName;

            if (TryGetAppLock())
            {
                startAtId = getStartAtId();

                IQueryable <StorableEvent> eventQuery =
                    applyFilter(dbContext.Events.AsNoTracking())
                    .Where(e => e.Id >= startAtId)
                    .OrderBy(e => e.Id);
                var oldCommandTimeout = dbContext.Database.CommandTimeout;
                try
                {
                    dbContext.Database.CommandTimeout = TimeSpan.FromMinutes(10).Seconds;
                    TotalMatchedEventCount            = eventQuery.Count();
                }
                catch
                {
                    System.Diagnostics.Trace.WriteLine("Failed :eventQuery.Count() \n" + eventQuery.ToString());
                    throw;
                }
                finally
                {
                    dbContext.Database.CommandTimeout = oldCommandTimeout;
                }
                BatchMatchedEventCount = Math.Min(BatchSize, TotalMatchedEventCount);

                eventQuery = eventQuery.Take(batchSize);

                events = DurableStreamFrom(eventQuery, startAtId);
            }
            else
            {
                events = Enumerable.Empty <StorableEvent>();
            }
        }
        public static void EnsureEventStoreIsInitialized()
        {
            if (!eventStoreInitialized)
            {
                EventStoreDbContext.NameOrConnectionString =
                    @"Data Source=(localdb)\MSSQLLocalDB; Integrated Security=True; MultipleActiveResultSets=False; Initial Catalog=ItsCqrsTestsEventStore";

                using (var eventStore = new EventStoreDbContext())
                {
                    new EventStoreDatabaseInitializer<EventStoreDbContext>().InitializeDatabase(eventStore);
                }
            }

            eventStoreInitialized = true;
        }
Beispiel #4
0
        /// <summary>
        /// Seeds an event store using JSON-serialized events stored in a file.
        /// </summary>
        public static void SeedFromFile(this EventStoreDbContext context, FileInfo file)
        {
            using (var stream = file.OpenRead())
                using (var reader = new StreamReader(stream))
                {
                    var json   = reader.ReadToEnd();
                    var events = Serializer.FromJsonToEvents(json).ToArray();

                    foreach (var e in events)
                    {
                        context.Events.Add(e.ToStorableEvent());

                        // it's necessary to save at every event to preserve ordering, otherwise EF will reorder them
                        context.SaveChanges();
                    }
                }
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ExclusiveEventStoreCatchupQuery"/> class.
        /// </summary>
        /// <param name="dbContext">The event store database context to execute the query against.</param>
        /// <param name="lockResourceName">Name of the lock. Multiple instances compete with other instances having the same <paramref name="lockResourceName" />.</param>
        /// <param name="getStartAtId">The id of the first event to query.</param>
        /// <param name="matchEvents">Specifies the event types to include the query. If none are specified, all events are queried.</param>
        public ExclusiveEventStoreCatchupQuery(EventStoreDbContext dbContext, string lockResourceName, Func <long> getStartAtId, MatchEvent[] matchEvents)
        {
            this.dbContext        = dbContext;
            this.lockResourceName = lockResourceName;

            if (TryGetAppLock())
            {
                startAtId = getStartAtId();
                IQueryable <StorableEvent> eventQuery = dbContext.Events;

                matchEvents = matchEvents ?? new[] { new MatchEvent() };

                // if specific event types are requested, we can optimize the event store query
                // if Event or IEvent are requested, we don't filter -- this requires reading every event
                if (matchEvents.Any())
                {
                    var eventTypes = matchEvents.Select(m => m.Type).Distinct().ToArray();
                    var aggregates = matchEvents.Select(m => m.StreamName).Distinct().ToArray();

                    if (!aggregates.Contains(MatchEvent.Wildcard))
                    {
                        var aggregateWildCard = aggregates.Any(string.IsNullOrWhiteSpace) ||
                                                aggregates.Contains(MatchEvent.Wildcard);

                        var eventWildCard = eventTypes.Any(string.IsNullOrWhiteSpace) ||
                                            eventTypes.Contains(MatchEvent.Wildcard);

                        eventQuery = eventQuery.Where(e => (aggregateWildCard || aggregates.Contains(e.StreamName)) &&
                                                      (eventWildCard || eventTypes.Contains(e.Type)));
                    }
                }

                Debug.WriteLine(new { eventQuery });

                expectedNumberOfEvents = eventQuery.Count(e => e.Id >= startAtId);
                events = DurableStreamFrom(eventQuery, startAtId);
            }
            else
            {
                events = Enumerable.Empty <StorableEvent>();
            }
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ExclusiveEventStoreCatchupQuery"/> class.
        /// </summary>
        /// <param name="dbContext">The event store database context to execute the query against.</param>
        /// <param name="lockResourceName">Name of the lock. Multiple instances compete with other instances having the same <paramref name="lockResourceName" />.</param>
        /// <param name="getStartAtId">The id of the first event to query.</param>
        /// <param name="matchEvents">Specifies the event types to include the query. If none are specified, all events are queried.</param>
        public ExclusiveEventStoreCatchupQuery(EventStoreDbContext dbContext, string lockResourceName, Func<long> getStartAtId, MatchEvent[] matchEvents)
        {
            this.dbContext = dbContext;
            this.lockResourceName = lockResourceName;

            if (TryGetAppLock())
            {
                startAtId = getStartAtId();
                IQueryable<StorableEvent> eventQuery = dbContext.Events;

                matchEvents = matchEvents ?? new[] { new MatchEvent() };

                // if specific event types are requested, we can optimize the event store query
                // if Event or IEvent are requested, we don't filter -- this requires reading every event
                if (matchEvents.Any())
                {
                    var eventTypes = matchEvents.Select(m => m.Type).Distinct().ToArray();
                    var aggregates = matchEvents.Select(m => m.StreamName).Distinct().ToArray();

                    if (!aggregates.Contains(MatchEvent.Wildcard))
                    {
                        var aggregateWildCard = aggregates.Any(string.IsNullOrWhiteSpace) ||
                                                                aggregates.Contains(MatchEvent.Wildcard);

                        var eventWildCard = eventTypes.Any(string.IsNullOrWhiteSpace) ||
                                                                eventTypes.Contains(MatchEvent.Wildcard);

                        eventQuery = eventQuery.Where(e => (aggregateWildCard || aggregates.Contains(e.StreamName)) &&
                                                           (eventWildCard || eventTypes.Contains(e.Type)));
                    }
                }

                this.eventQuery = eventQuery;
                
                expectedNumberOfEvents = eventQuery.Count(e => e.Id >= startAtId);
                events = DurableStreamFrom(eventQuery, startAtId);
            }
            else
            {
                events = Enumerable.Empty<StorableEvent>();
            }
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ExclusiveEventStoreCatchupQuery" /> class.
        /// </summary>
        /// <param name="dbContext">The event store database context to execute the query against.</param>
        /// <param name="lockResourceName">Name of the lock. Multiple instances compete with other instances having the same <paramref name="lockResourceName" />.</param>
        /// <param name="getStartAtId">The id of the first event to query.</param>
        /// <param name="matchEvents">Specifies the event types to include the query. If none are specified, all events are queried.</param>
        /// <param name="batchSize">The number of events queried from the event store at each iteration.</param>
        /// <param name="filter">An optional filter expression to constrain the query that the catchup uses over the event store.</param>
        public ExclusiveEventStoreCatchupQuery(
            EventStoreDbContext dbContext,
            string lockResourceName,
            Func <long> getStartAtId,
            MatchEvent[] matchEvents,
            int batchSize = 10000,
            Expression <Func <StorableEvent, bool> > filter = null)
        {
            if (batchSize < 1)
            {
                throw new ArgumentException($"{nameof(batchSize)} must be greater than zero.");
            }

            this.dbContext        = dbContext;
            this.lockResourceName = lockResourceName;

            if (TryGetAppLock())
            {
                startAtId = getStartAtId();
                IQueryable <StorableEvent> eventQuery = dbContext.Events.AsNoTracking();

                matchEvents = matchEvents ?? new[] { new MatchEvent() };

                // if specific event types are requested, we can optimize the event store query
                // if Event or IEvent are requested, we don't filter -- this requires reading every event
                if (matchEvents.Any())
                {
                    var eventTypes = matchEvents.Select(m => m.Type).Distinct().ToArray();
                    var aggregates = matchEvents.Select(m => m.StreamName).Distinct().ToArray();

                    if (!aggregates.Any(streamName => string.IsNullOrWhiteSpace(streamName) || streamName == MatchEvent.Wildcard))
                    {
                        if (!eventTypes.Any(type => string.IsNullOrWhiteSpace(type) || type == MatchEvent.Wildcard))
                        {
                            // Filter on StreamName and Type
                            var projectionEventFilter = new CatchupEventFilter(matchEvents);
                            eventQuery = eventQuery.Where(projectionEventFilter.Filter);
                        }
                        else
                        {
                            // Filter on StreamName
                            eventQuery = eventQuery.Where(e => aggregates.Contains(e.StreamName));
                        }
                    }
                }

                if (filter != null)
                {
                    eventQuery = eventQuery.Where(filter);
                }

                eventQuery = eventQuery
                             .Where(e => e.Id >= startAtId)
                             .Take(batchSize);

                expectedNumberOfEvents = eventQuery.Count();

                events = DurableStreamFrom(eventQuery, startAtId);
            }
            else
            {
                events = Enumerable.Empty <StorableEvent>();
            }
        }
Beispiel #8
0
        /// <summary>
        /// Initializes a new instance of the <see cref="AppLock"/> class.
        /// </summary>
        /// <param name="db">The database.</param>
        /// <param name="lockResourceName">The lock resource.</param>
        public AppLock(EventStoreDbContext db, string lockResourceName)
        {
            this.db = db;
            this.lockResourceName = lockResourceName;
            connection = db.OpenConnection();

            const string cmd = @"
DECLARE @result int;
EXEC @result = sp_getapplock @Resource = @lockResource,
                                @LockMode = 'Exclusive',
                                @LockOwner = 'Session',
                                @LockTimeout = 60000;
SELECT @result";

            var result = -1000;

            using (var getAppLock = connection.CreateCommand())
            {
                getAppLock.Parameters.Add(new SqlParameter("lockResource", lockResourceName));
                getAppLock.CommandText = cmd;
                
                try
                {
                    Debug.WriteLine("Trying to acquire app lock '{0}' (#{1})", lockResourceName, GetHashCode());

                    result = (int)getAppLock.ExecuteScalar();
                }
                catch (SqlException exception)
                {
                    if (exception.Message.StartsWith("Timeout expired."))
                    {
                        Debug.WriteLine("Timeout expired waiting for sp_getapplock. (#{0})", GetHashCode());
                        DebugWriteLocks();
                        return;
                    }

                    throw;
                }
            }

            resultCode = result;

            if (result >= 0)
            {
                Debug.WriteLine("Acquired app lock '{0}' with result {1} (#{2})",
                                              lockResourceName,
                                              result,
                                              GetHashCode());
            }
            else
            {
                Debug.WriteLine("Failed to acquire app lock '{0}' with code {1} (#{2})",
                                              lockResourceName,
                                              result,
                                              GetHashCode());
            }

            disposables = Disposable.Create(OnDispose);

#if DEBUG
            Active[this] = this;
#endif
        }
Beispiel #9
0
        /// <summary>
        /// Initializes a new instance of the <see cref="AppLock"/> class.
        /// </summary>
        /// <param name="db">The database.</param>
        /// <param name="lockResourceName">The lock resource.</param>
        public AppLock(EventStoreDbContext db, string lockResourceName)
        {
            this.db = db;
            this.lockResourceName = lockResourceName;
            connection            = db.OpenConnection();

            const string cmd = @"
DECLARE @result int;
EXEC @result = sp_getapplock @Resource = @lockResource,
                                @LockMode = 'Exclusive',
                                @LockOwner = 'Session',
                                @LockTimeout = 60000;
SELECT @result";

            var result = -1000;

            using (var getAppLock = connection.CreateCommand())
            {
                getAppLock.Parameters.Add(new SqlParameter("lockResource", lockResourceName));
                getAppLock.CommandText = cmd;

                try
                {
                    Debug.WriteLine("Trying to acquire app lock '{0}' (#{1})", lockResourceName, GetHashCode());

                    result = (int)getAppLock.ExecuteScalar();
                }
                catch (SqlException exception)
                {
                    if (exception.Message.StartsWith("Timeout expired."))
                    {
                        Debug.WriteLine("Timeout expired waiting for sp_getapplock. (#{0})", GetHashCode());
                        DebugWriteLocks();
                        return;
                    }

                    throw;
                }
            }

            resultCode = result;

            if (result >= 0)
            {
                Debug.WriteLine("Acquired app lock '{0}' with result {1} (#{2})",
                                lockResourceName,
                                result,
                                GetHashCode());
            }
            else
            {
                Debug.WriteLine("Failed to acquire app lock '{0}' with code {1} (#{2})",
                                lockResourceName,
                                result,
                                GetHashCode());
            }

            disposables = Disposable.Create(OnDispose);

#if DEBUG
            Active[this] = this;
#endif
        }
        public void Related_events_are_available_via_diagnostics_endpoint()
        {
            // arrange
            var relatedId1 = Any.Guid();
            var relatedId2 = Any.Guid();
            var relatedId3 = Any.Guid();
            var relatedId4 = Any.Guid();
            var unrelatedId = Any.Guid();

            Console.WriteLine(new
            {
                relatedId1,
                relatedId2,
                relatedId3,
                relatedId4,
                unrelatedId
            }.ToLogString());

            using (var db = new EventStoreDbContext())
            {
                Enumerable.Range(1, 20).ForEach(i => db.Events.Add(new StorableEvent
                {
                    AggregateId = relatedId1,
                    SequenceNumber = i,
                    Body = new { relatedId2 }.ToJson(),
                    Timestamp = Clock.Now(),
                    StreamName = "one",
                    Type = "Event" + i.ToString()
                }));

                Enumerable.Range(1, 20).ForEach(i => db.Events.Add(new StorableEvent
                {
                    AggregateId = relatedId2,
                    SequenceNumber = i,
                    Body = new { relatedId3 }.ToJson(),
                    Timestamp = Clock.Now(),
                    StreamName = "two",
                    Type = "Event" + i.ToString()
                }));

                Enumerable.Range(1, 20).ForEach(i => db.Events.Add(new StorableEvent
                {
                    AggregateId = relatedId3,
                    SequenceNumber = i,
                    Body = new { relatedId4 }.ToJson(),
                    Timestamp = Clock.Now(),
                    StreamName = "three",
                    Type = "Event" + i.ToString()
                }));

                Enumerable.Range(1, 20).ForEach(i => db.Events.Add(new StorableEvent
                {
                    AggregateId = relatedId4,
                    SequenceNumber = i,
                    Body = new { }.ToJson(),
                    Timestamp = Clock.Now(),
                    StreamName = "three",
                    Type = "Event" + i.ToString()
                }));

                Enumerable.Range(1, 20).ForEach(i => db.Events.Add(new StorableEvent
                {
                    AggregateId = unrelatedId,
                    SequenceNumber = i,
                    Body = new { }.ToJson(),
                    Timestamp = Clock.Now(),
                    StreamName = "three",
                    Type = "Event" + i.ToString()
                }));

                db.SaveChanges();
            }

            var client = CreateClient();

            // act
            var response = client.GetAsync("http://contoso.com/api/events/related/" + relatedId1)
                .Result
                .ShouldSucceed()
                .JsonContent();

            Console.WriteLine(response);

            // assert
            //            events.Count().Should().Be(80);
            //            events.Should().Contain(e => e.AggregateId == relatedId1);
            //            events.Should().Contain(e => e.AggregateId == relatedId2);
            //            events.Should().Contain(e => e.AggregateId == relatedId3);
            //            events.Should().Contain(e => e.AggregateId == relatedId4);
            //            events.Should().NotContain(e => e.AggregateId == unrelatedId);

            // TODO: (Related_events_are_available_via_diagnostics_endpoint) write test
            Assert.Fail("Test not written yet.");
        }
Beispiel #11
0
 /// <summary>
 /// Returns the Id of the latest event written to the event store, or 0 if the event store is empty.
 /// </summary>
 public static long HighestEventId(this EventStoreDbContext db) =>
 db.Events.Max <StorableEvent, long?>(e => e.Id) ?? 0;