Skip to content
This repository has been archived by the owner on Nov 9, 2022. It is now read-only.

Entity Framework #1

Open
CumpsD opened this issue Apr 26, 2018 · 7 comments
Open

Entity Framework #1

CumpsD opened this issue Apr 26, 2018 · 7 comments

Comments

@CumpsD
Copy link
Collaborator

CumpsD commented Apr 26, 2018

Nice library, thanks!

How would you use this with Entity Framework?

@bcuff
Copy link
Owner

bcuff commented Apr 26, 2018

It's been a while since i've used entity framework. I assume it uses ADO .NET under the hood. If that's the case and it offers a way to provide an alternative DbConnection object then you could inject a TraceDbConnection instead.

@CumpsD
Copy link
Collaborator Author

CumpsD commented Apr 26, 2018

I've tried this, but apparently it needs a DbConnection and TraceDbConnection is an IDbConnection

                services
                    .AddDbContextPool<BackofficeContext>(options => options
                        .UseLoggerFactory(loggerFactory)
                        .UseSqlServer(new TraceDbConnection(new SqlConnection(backofficeProjectionsConnectionString)), sqlServerOptions =>
                        {
                            sqlServerOptions.EnableRetryOnFailure();
                            sqlServerOptions.MigrationsHistoryTable(MigrationTables.Backoffice, Schema.Backoffice);
                        }));

@CumpsD
Copy link
Collaborator Author

CumpsD commented Apr 26, 2018

Maybe this can help you:

namespace DataDog.Tracing.Sql
{
    using System;
    using System.Data;
    using System.Data.Common;

    public class TraceDbConnection : DbConnection
    {
        private const string ServiceName = "sql";

        readonly ISpanSource _spanSource;
        readonly DbConnection _connection;

        public TraceDbConnection(DbConnection connection)
            : this(connection, TraceContextSpanSource.Instance) { }

        public TraceDbConnection(DbConnection connection, ISpanSource spanSource)
        {
            _connection = connection ?? throw new ArgumentNullException(nameof(connection));
            _spanSource = spanSource ?? throw new ArgumentNullException(nameof(spanSource));
        }

        public IDbConnection InnerConnection => _connection;

        public new void Dispose() => _connection.Dispose();

        protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) => _connection.BeginTransaction(isolationLevel);

        protected override DbCommand CreateDbCommand() => new TraceDbCommand(_connection.CreateCommand(), _spanSource);

        public override void ChangeDatabase(string databaseName) => _connection.ChangeDatabase(databaseName);

        public override void Close() => _connection.Close();

        public override void Open()
        {
            var span = _spanSource.Begin("sql.connect", ServiceName, _connection.Database, ServiceName);
            try
            {
                _connection.Open();
            }
            catch (Exception ex)
            {
                span?.SetError(ex);
                throw;
            }
            finally
            {
                span?.Dispose();
            }
        }

        public override string ConnectionString
        {
            get => _connection.ConnectionString;
            set => _connection.ConnectionString = value;
        }

        public override int ConnectionTimeout => _connection.ConnectionTimeout;

        public override string Database => _connection.Database;

        public override string DataSource => _connection.DataSource;

        public override string ServerVersion => _connection.ServerVersion;

        public override ConnectionState State => _connection.State;
    }

    public class TraceDbCommand : DbCommand
    {
        private const string ServiceName = "sql";
        private readonly DbCommand _command;
        private readonly ISpanSource _spanSource;

        public TraceDbCommand(DbCommand command)
            : this(command, TraceContextSpanSource.Instance) { }

        public TraceDbCommand(DbCommand command, ISpanSource spanSource)
        {
            _command = command;
            _spanSource = spanSource;
        }

        public IDbCommand InnerCommand => _command;

        public new void Dispose() => _command.Dispose();

        public override void Cancel() => _command.Cancel();

        protected override DbParameter CreateDbParameter() => _command.CreateParameter();

        protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
        {
            const string name = "sql." + nameof(ExecuteReader);
            var span = _spanSource.Begin(name, ServiceName, _command.Connection.Database, ServiceName);
            try
            {
                if (span != null)
                {
                    const string metaKey = "sql." + nameof(CommandBehavior);
                    span.SetMeta(metaKey, behavior.ToString("x"));
                    SetMeta(span);
                }
                return _command.ExecuteReader(behavior);
            }
            catch (Exception ex)
            {
                span?.SetError(ex);
                throw;
            }
            finally
            {
                span?.Dispose();
            }
        }

        private void SetMeta(ISpan span)
        {
            span.SetMeta("sql.CommandText", CommandText);
            span.SetMeta("sql.CommandType", CommandType.ToString());
        }

        public override int ExecuteNonQuery()
        {
            const string name = "sql." + nameof(ExecuteNonQuery);
            var span = _spanSource.Begin(name, ServiceName, _command.Connection.Database, ServiceName);
            try
            {
                var result = _command.ExecuteNonQuery();
                if (span != null)
                {
                    span.SetMeta("sql.RowsAffected", result.ToString());
                    SetMeta(span);
                }
                return result;
            }
            catch (Exception ex)
            {
                span?.SetError(ex);
                throw;
            }
            finally
            {
                span?.Dispose();
            }
        }

        public override object ExecuteScalar()
        {
            const string name = "sql." + nameof(ExecuteScalar);
            var span = _spanSource.Begin(name, ServiceName, _command.Connection.Database, ServiceName);
            try
            {
                if (span != null) SetMeta(span);
                return _command.ExecuteScalar();
            }
            catch (Exception ex)
            {
                span?.SetError(ex);
                throw;
            }
            finally
            {
                span?.Dispose();
            }
        }

        public override void Prepare() => _command.Prepare();

        protected override DbParameterCollection DbParameterCollection => _command.Parameters;

        public override bool DesignTimeVisible { get; set; }

        public override string CommandText
        {
            get => _command.CommandText;
            set => _command.CommandText = value;
        }

        public override int CommandTimeout
        {
            get => _command.CommandTimeout;
            set => _command.CommandTimeout = value;
        }

        public override CommandType CommandType
        {
            get => _command.CommandType;
            set => _command.CommandType = value;
        }

        protected override DbConnection DbConnection
        {
            get => _command.Connection;
            set => _command.Connection = value;
        }

        protected override DbTransaction DbTransaction
        {
            get => _command.Transaction;
            set => _command.Transaction = value;
        }

        public override UpdateRowSource UpdatedRowSource
        {
            get => _command.UpdatedRowSource;
            set => _command.UpdatedRowSource = value;
        }
    }
}

@CumpsD
Copy link
Collaborator Author

CumpsD commented Apr 26, 2018

@bcuff I can confirm with the above code I can trace EF :)

Perhaps it is something which can be added?

@bcuff
Copy link
Owner

bcuff commented Apr 26, 2018

@CumpsD sure. Feel free to submit that as a pull request.

@bcuff
Copy link
Owner

bcuff commented Apr 26, 2018

I invited you to the repository so can push your changes to a new branch without having to fork.

@CumpsD
Copy link
Collaborator Author

CumpsD commented Apr 29, 2018

There you go: #4

I also added 3 smaller PRs

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants