internal static void BuildSqlMetaData(PayloadFormatting payloadFormatting)
        {
            var payloadMetaData = payloadFormatting == PayloadFormatting.Json
                ? new SqlMetaData("Payload", SqlDbType.NVarChar, 4000)
                : new SqlMetaData("Payload", SqlDbType.Xml);

            SqlMetaData = new[]
            {
                new SqlMetaData("InstanceName", SqlDbType.NVarChar, 1000),
                new SqlMetaData("ProviderId", SqlDbType.UniqueIdentifier),
                new SqlMetaData("ProviderName", SqlDbType.NVarChar, 500),
                new SqlMetaData("EventId", SqlDbType.Int),
                new SqlMetaData("EventKeywords", SqlDbType.BigInt),
                new SqlMetaData("Level", SqlDbType.Int),
                new SqlMetaData("Opcode", SqlDbType.Int),
                new SqlMetaData("Task", SqlDbType.Int),
                new SqlMetaData("Timestamp", SqlDbType.DateTimeOffset),
                new SqlMetaData("Version", SqlDbType.Int),
                new SqlMetaData("FormattedMessage", SqlDbType.NVarChar, 4000),
                payloadMetaData,
                new SqlMetaData("ActivityId", SqlDbType.UniqueIdentifier),
                new SqlMetaData("RelatedActivityId", SqlDbType.UniqueIdentifier),
                new SqlMetaData("ProcessId", SqlDbType.Int),
                new SqlMetaData("ThreadId", SqlDbType.Int)
            };

            Fields = SqlMetaData.Select(x => x.Name).ToArray();
        }
        internal static SqlDataRecord ToSqlDataRecord(this EventEntry record, string instanceName, PayloadFormatting payloadFormatting)
        {
            var sqlDataRecord = new SqlDataRecord(SqlMetaData);
            var payloadValue = payloadFormatting == PayloadFormatting.Json
                ? EventEntryUtil.JsonSerializePayload(record)
                : EventEntryUtil.XmlSerializePayload(record);

            sqlDataRecord.SetValue(0, instanceName ?? string.Empty);
            sqlDataRecord.SetValue(1, record.ProviderId);
            sqlDataRecord.SetValue(2, record.Schema.ProviderName ?? string.Empty);
            sqlDataRecord.SetValue(3, record.EventId);
            sqlDataRecord.SetValue(4, (long)record.Schema.Keywords);
            sqlDataRecord.SetValue(5, (int)record.Schema.Level);
            sqlDataRecord.SetValue(6, (int)record.Schema.Opcode);
            sqlDataRecord.SetValue(7, (int)record.Schema.Task);
            sqlDataRecord.SetValue(8, record.Timestamp);
            sqlDataRecord.SetValue(9, record.Schema.Version);
            sqlDataRecord.SetValue(10, (object)record.FormattedMessage ?? DBNull.Value);
            sqlDataRecord.SetValue(11, (object)payloadValue ?? DBNull.Value);
            sqlDataRecord.SetValue(12, record.ActivityId);
            sqlDataRecord.SetValue(13, record.RelatedActivityId);
            sqlDataRecord.SetValue(14, record.ProcessId);
            sqlDataRecord.SetValue(15, record.ThreadId);

            return sqlDataRecord;
        }
        public EventEntryDataReader(IEnumerable<EventEntry> collection, string instanceName, PayloadFormatting payloadFormatting)
        {
            this.enumerator = collection.GetEnumerator();
            this.instanceName = instanceName;
            this.payloadFormatting = payloadFormatting;

            EventEntryExtensions.BuildSqlMetaData(payloadFormatting);
        }
        /// <summary>
        /// Subscribes to an <see cref="IObservable{EventEntry}"/> using a <see cref="SqlDatabaseSink"/>.
        /// </summary>
        /// <param name="eventStream">The event stream. Typically this is an instance of <see cref="ObservableEventListener" />.</param>
        /// <param name="instanceName">The name of the instance originating the entries.</param>
        /// <param name="connectionString">The connection string.</param>
        /// <param name="tableName">The name of the table.</param>
        /// <param name="bufferingInterval">The buffering interval between each batch publishing. Default value is <see cref="Buffering.DefaultBufferingInterval"/>.</param>
        /// <param name="bufferingCount">The number of entries that will trigger a batch publishing.</param>
        /// <param name="onCompletedTimeout">Defines a timeout interval for when flushing the entries after an <see cref="SqlDatabaseSink.OnCompleted"/> call is received and before disposing the sink.
        /// This means that if the timeout period elapses, some event entries will be dropped and not sent to the store. Normally, calling <see cref="IDisposable.Dispose"/> on 
        /// the <see cref="System.Diagnostics.Tracing.EventListener"/> will block until all the entries are flushed or the interval elapses.
        /// If <see langword="null"/> is specified, then the call will block indefinitely until the flush operation finishes.</param>
        /// <param name="maxBufferSize">The maximum number of entries that can be buffered while it's sending to SQL Database before the sink starts dropping entries.
        /// This means that if the timeout period elapses, some event entries will be dropped and not sent to the store. Normally, calling <see cref="IDisposable.Dispose" /> on
        /// the <see cref="System.Diagnostics.Tracing.EventListener" /> will block until all the entries are flushed or the interval elapses.
        /// If <see langword="null" /> is specified, then the call will block indefinitely until the flush operation finishes.</param>
        /// <param name="payloadFormatting">The formatting of the payload data.</param>
        /// <returns>A subscription to the sink that can be disposed to unsubscribe the sink and dispose it, or to get access to the sink instance.</returns>
        public static SinkSubscription<SqlDatabaseSink> LogToSqlDatabase(this IObservable<EventEntry> eventStream, string instanceName, string connectionString, string tableName = DefaultTableName, TimeSpan? bufferingInterval = null, int bufferingCount = Buffering.DefaultBufferingCount, TimeSpan? onCompletedTimeout = null, int maxBufferSize = Buffering.DefaultMaxBufferSize, PayloadFormatting payloadFormatting = PayloadFormatting.Json)
        {
            var sink = new SqlDatabaseSink(
                instanceName,
                connectionString,
                tableName,
                bufferingInterval ?? Buffering.DefaultBufferingInterval,
                bufferingCount,
                maxBufferSize,
                onCompletedTimeout ?? Timeout.InfiniteTimeSpan,
                payloadFormatting);

            var subscription = eventStream.Subscribe(sink);

            return new SinkSubscription<SqlDatabaseSink>(subscription, sink);
        }
 /// <summary>
 /// Subscribes to the listener using a <see cref="SqlDatabaseSink" />.
 /// </summary>
 /// <param name="instanceName">The name of the instance originating the entries.</param>
 /// <param name="connectionString">The connection string.</param>
 /// <param name="tableName">The name of the table.</param>
 /// <param name="bufferingInterval">The buffering interval between each batch publishing.</param>
 /// <param name="bufferingCount">The number of entries that will trigger a batch publishing.</param>
 /// <param name="listenerDisposeTimeout">Defines a timeout interval for the flush operation when the listener is disposed.
 /// This means that if the timeout period elapses, some event entries will be dropped and not sent to the store. Calling <see cref="IDisposable.Dispose"/> on 
 /// the <see cref="EventListener"/> will block until all the entries are flushed or the interval elapses.
 /// If <see langword="null"/> is specified, then the call will block indefinitely until the flush operation finishes.</param>
 /// <param name="maxBufferSize">The maximum number of entries that can be buffered while it's sending to SQL Database before the sink starts dropping entries.
 /// This means that if the timeout period elapses, some event entries will be dropped and not sent to the store. Normally, calling <see cref="IDisposable.Dispose" /> on
 /// the <see cref="System.Diagnostics.Tracing.EventListener" /> will block until all the entries are flushed or the interval elapses.
 /// If <see langword="null" /> is specified, then the call will block indefinitely until the flush operation finishes.</param>
 /// <param name="payloadFormatting">The formatting of the payload data.</param>
 /// <returns>An event listener that uses <see cref="SqlDatabaseSink"/> to log events.</returns>
 public static EventListener CreateListener(string instanceName, string connectionString, string tableName = DefaultTableName, TimeSpan? bufferingInterval = null, int bufferingCount = Buffering.DefaultBufferingCount, TimeSpan? listenerDisposeTimeout = null, int maxBufferSize = Buffering.DefaultMaxBufferSize, PayloadFormatting payloadFormatting = PayloadFormatting.Json)
 {
     var listener = new ObservableEventListener();
     listener.LogToSqlDatabase(instanceName, connectionString, tableName, bufferingInterval, bufferingCount, listenerDisposeTimeout, maxBufferSize, payloadFormatting);
     return listener;
 }