Повторное использование кода Стихи Продолжительность доступа к базе данных

Эта проблема

У меня есть некоторый код, который я кодировал таким образом, чтобы обеспечить высокую ремонтопригодность и возможность повторного использования кода. Я обеспокоен тем или иным фрагментом кода и хотел бы получить профессиональное мнение о том, будет ли этот код разрушаться при сильном стрессе.

Кодез ##

public abstract class PlexxisDataTransferObjects : PlexxisDatabaseRow
    {
        //static methods
        public static List<PlexxisDatabaseRow> GetAll();

        //Constructors
        public PlexxisDataTransferObjects(){ }

        //Methods
        public abstract bool Insert(OracleConnection Conn);
        public abstract bool Update(OracleConnection Conn);
        public abstract bool Delete(OracleConnection Conn);

        public bool Insert()
        {
            using (var Conn = new OracleConnection(ConnectionString))
            {
                Conn.Open();
                return Insert(Conn);
            }
        }
        public bool Update()
        {
            using (var Conn = new OracleConnection(ConnectionString))
            {
                Conn.Open();
                return Update(Conn);
            }
        }
        public  bool Delete()
        {
            using (var Conn = new OracleConnection(ConnectionString))
            {
                Conn.Open();
                return Delete(Conn);
            }
        }
    }


    //Data Transfer Objects
    public sealed class Apps : PlexxisDataTransferObjects
    {
        //Static Methods
        public override static List<PlexxisDatabaseRow> GetAll()
        {
            List<PlexxisDatabaseRow> collection = new List<PlexxisDatabaseRow>();
            using (var Conn = new OracleConnection(ConnectionString))
            {
                using (var Command = new OracleCommand("select * from APPS", Conn))
                {
                    Conn.Open();
                    using (var reader = Command.ExecuteReader(CommandBehavior.CloseConnection))
                        while (reader.Read())
                            collection.Add(new Apps(reader));
                }
            }
            return collection;
        }

        //Fields
        public int AppId;
        public string AuthKey;
        public string Title;
        public string Description;
        public bool isClientCustomApp;

        //Constructors
        public Apps() : base () { }
        public Apps(OracleDataReader reader) : base ()
        {
            if (reader["APP_ID"] != DBNull.Value)
                this.AppId = Convert.ToInt32(reader["APP_ID"]);

            if (reader["AUTH_KEY"] != DBNull.Value)
                this.AuthKey = Convert.ToString(reader["AUTH_KEY"]);

            if (reader["TITLE"] != DBNull.Value)
                this.Title = Convert.ToString(reader["TITLE"]);

            if (reader["DESCRIPTION"] != DBNull.Value)
                this.Description = Convert.ToString(reader["DESCRIPTION"]);

            if (reader["IS_CLIENT_CUSTOM_APP"] != DBNull.Value)
                this.isClientCustomApp = Convert.ToBoolean(reader["IS_CLIENT_CUSTOM_APP"]);
        }

        //Methods
        public override bool Insert(OracleConnection Conn)
        {

            string sql = string.Empty;
            sql += "INSERT INTO APPS (APP_ID, AUTH_KEY, TITLE, DESCRIPTION, IS_CLIENT_CUSTOM_APP)";
            sql += "VALUES(:appid, :authkey, :title, :description, :iscust)";

            using (var Command = new OracleCommand(sql, Conn))
            {
                AppId = GetId();
                Command.Parameters.Add(":appid", OracleDbType.Int32).Value = AppId;
                Command.Parameters.Add(":authkey", OracleDbType.Varchar2).Value = AuthKey;
                Command.Parameters.Add(":title", OracleDbType.Varchar2).Value = Title;
                Command.Parameters.Add(":description", OracleDbType.Varchar2).Value = Description;
                Command.Parameters.Add(":iscust", OracleDbType.Int32).Value = Convert.ToInt32(isClientCustomApp);

                return Convert.ToBoolean(Command.ExecuteNonQuery());
            }
        }
        public override bool Update(OracleConnection Conn)
        {
            string sql = string.Empty;
            sql += "UPDATE APPS SET ";
            sql += "AUTH_KEY = :authkey, TITLE = :title, DESCRIPTION = :description, IS_CLIENT_CUSTOM_APP = :iscust ";
            sql += "WHERE APP_ID = :appid";

            using (var Command = new OracleCommand(sql, Conn))
            {
                Command.Parameters.Add(":authkey", OracleDbType.Varchar2).Value = AuthKey;
                Command.Parameters.Add(":title", OracleDbType.Varchar2).Value = Title;
                Command.Parameters.Add(":description", OracleDbType.Varchar2).Value = Description;
                Command.Parameters.Add(":iscust", OracleDbType.Int32).Value = Convert.ToInt32(isClientCustomApp);
                Command.Parameters.Add(":appid", OracleDbType.Int32).Value = AppId;

                return Convert.ToBoolean(Command.ExecuteNonQuery());
            }
        }
        public override bool Delete(OracleConnection Conn)
        {
            string sql = string.Empty;
            sql += "DELETE FROM APPS ";
            sql += "WHERE APP_ID = :appid";

            using (var Command = new OracleCommand(sql, Conn))
            {
                Command.Parameters.Add(":appid", OracleDbType.Int32).Value = AppId;
                return Convert.ToBoolean(Command.ExecuteNonQuery());
            }
        }
    }

На что я смотрю?

Больше всего меня интересует метод Insert, Update и Delete в абстрактном классе, вызывающий Insert, Update и Delete в конкретном классе.

Я сделал это таким образом, чтобы при необходимости разрешить транзакции, открыв соединение и явно запустив транзакцию, отправив транзакцию и оставив объекты делать то, что им нужно; Более того, если бы мне пришлось явно переписать 3 метода для примерно 40 классов, это могло бы стать довольно громоздким.

Тем не менее, я обеспокоен тем, что, открыв соединение раньше, я могу задержать базу данных. Я не знаю, сколько входных данных может обновляться в любой момент времени. В этой ситуации у меня есть две основные мысли: я могу либо сделать вставку, обновить и удалить абстрактный в абстрактном классе и реализовать их, в то же время явно открывая соединение непосредственно перед Command.ExecuteNonQuery(), либо я могу оставить все как есть.

Что бы я хотел от вас?

В первую очередь, ваше мнение о ситуации. Во-вторых, было бы очень полезно указать на любые подводные камни в логике или на плохое кодирование, которое вы обнаружили.

2 ответа

Решение

Я думаю, что это могло бы стоить исследовать образец единицы работы.

Мне кажется, что вы уже используете шаблон активной записи, однако я нахожу проблематичным (с точки зрения разделения интересов и зависимости), что ваши определения классов жестко запрограммированы, чтобы зависеть от оракула, что означает код, который использует ваши DTOs также должны зависеть от оракула. Я не утверждаю, что это проблема в случае, если вы хотите переключить свою базу данных, я говорю, что лучше иметь очень разделенную систему, как для понимания, так и для модульного тестирования.

Код агностической базы данных

class Application
{
    public int ID { get; set; }
    public string AuthKey { get; set; }
    // and so on
}

interface IApplicationRepository
{
    IEnumerable<Application> GetAll();
    void Update(Application app);
    void Delete(Application app);
    void Insert(Application app);
}

interface IUnitOfWork : IDisposable
{
    IApplicationRepository Applications { get; }
    void Commit();
}

Код потребления

void SaveButton_Click(object sender, EventArgs e)
{
    // this could be resolved by dependency injection, this would know about Oracle
    using (var uow = UnitOfWorkFactory.Create()) 
    {
        uow.Applications.Insert(new Application { AuthKey = "1234" });

        // you may have other repo that have work done in the same transaction / connection

        uow.Commit();
    }
}

Если вы посмотрите на весь код, который написан выше, там нет упоминаний об Oracle или даже о концепции соединений или транзакций. У вас есть эта абстракция называется UnitOfWork который за кулисами управляет состоянием вашего приложения. Репозиторий работает с простыми классами. Этот тип кода легко издеваться и писать тесты для. Это огромно для удобства обслуживания.

Код базы данных

class OracleApplicationRepository : IApplicationRepository
{
    public readonly OracleDbConnection _dbConnection;

    public OracleApplicationRepository(OracleDbConnection dbConnection)
    {
        _dbConnection = dbConnection;
    }

    IEnumerable<Application> GetAll()
    {
        // up to you, the viewer
        throw new NotImplementedException();
    }

    void Update(Application app)
    {
        // up to the viewer
    }

    void Delete(Application app)
    {
        // up to the viewer
    }

    void Insert(Application app)
    {       
        using (var command = _dbConnection.CreateCommand())
        {
            // or whatever the syntax is
            command.Parameters["AuthKey"] = app.AuthKey;

            command.ExecuteNonQuery();
        }
    }
}

class OracleUnitOfWork : IUnitOfWork
{
    private readonly OracleDbConnection _dbConnection;

    public OracleUnitOfWork(string connectionString)
    {
        _dbConnection = new OracleDbConnection(connectionString);
    }

    public IApplicationRepository Applications 
    {
        get
        {
            // this could be lazy loaded instead of making new instances all over the place
            return new OracleApplicationRepository(_dbConnection); 
        }
    }

    public Dispose()
    {
        // close the connection and any transactions
        _dbConnection.Dispose();
    }
}

Открытие и закрытие соединения с базой данных для каждой операции CRUD повредит, если вы зацикливаетесь на большом наборе данных. Кроме того, вы должны использовать try, catch и, наконец,. Вы не можете гарантировать, что база данных будет работать, и это вызовет исключение.

Другие вопросы по тегам