Подтвердить что ты не робот

Как добиться разделения чтения/записи с Entity Framework

У меня есть настройка базы данных с использованием репликации master/slave. У меня есть один мастер и (по крайней мере) один раб, возможно, рабы. Для простоты здесь я расскажу об одном ведущем, одном подчиненном, потому что определение того, какой подчиненный использовать, включает некоторую бизнес-логику, не относящуюся к реальной проблеме.

Здесь схема установки (с помощью ведомых устройств):

Обзор

В приложении (в настоящее время используется Dapper) У меня есть следующий, упрощенный код:

abstract class BaseRepo
{
    private readonly string _readconn;
    private readonly string _writeconn;

    public BaseRepo(string readConnection, string writeConnection)
    {
        _readconn = readConnection;     //Actually IEnumerable<string> for ℕ slaves
        _writeconn = writeConnection;
    }

    private SqlConnection GetOpenConnection(string cnstring)
    {
        var c = new SqlConnection(cnstring);
        c.Open();
        return c;
    }

    public SqlConnection GetOpenReadConnection()
    {
        return this.GetOpenConnection(_readconn);
        // Actually we use some business-logic to determine *which* of the slaves to use
    }

    public SqlConnection GetOpenWriteConnection()
    {
        return this.GetOpenConnection(_writeconn);
    }
}

class CustomerRepo : BaseRepo
{
    // ...ctor left out for brevity...

    // "Read" functions use the "read" connection
    public IEnumerable<Customer> ListCustomers()
    {
        using (var c = this.GetOpenReadConnection())
        {
            return c.Query<Customer>("select * from customers order by name")
                    .AsEnumerable();
        }
    }

    // "Write" functions use the "write" connection
    public void UpdateCustomer(Customer cust)
    {
        using (var c = this.GetOpenWriteConnection())
        {
            c.Execute("update customers set name = @name where id = @id", cust);
        }
    }
}

Мой вопрос: предположим, что я хочу использовать Entity Framework ( "сначала код", если это необходимо) вместо Dapper; как мне лучше всего идти по той же концепции; вставки/обновления/удаления выполняются в базе данных "master", а выборки выполняются с ведомым (или любым из ведомых устройств). Поддерживает ли EF этот сценарий вообще? Что мне нужно сделать, чтобы сделать эту работу?


Дополнительная информация. Я уже использую "только для чтения" и "только для записи" на уровне SQL Server как "последнюю строку защиты", чтобы предотвратить ошибки в DAL. То, что я ищу, - это метод ограничения моего DAL, чтобы избежать необходимости исключать исключения SQL Server из-за "недопустимых" действий и необходимости сначала обращаться к (некорректному) SQL-серверу, прежде чем обнаруживать требуемое действие. не допускается. Я мог бы использовать тот же подход, что и сейчас; создать экземпляр/использовать правильный DbContext в самом методе (listcustomers/updatecustomer в приведенном выше примере). Я понимаю. Но это означало бы, что я должен был бы создать функцию "обертки" для каждого действия "CRUD" для каждого "сущности", что было бы причиной того, почему я перешел от dapper к EF в первую очередь; просто выставляйте DBSet и EF позаботитесь о чанстрекинг/SQL-запросах и т.д., и теперь, надеюсь, также выясните, какую строку подключения использовать для каждого действия.

4b9b3361

Ответ 1

Как было предложено другими, создайте контекст чтения/записи по умолчанию, а затем создайте только для чтения, наследуя его. Также обязательно выполняйте в частичном классе конструктор, принимающий другую конфигурацию, если вы тоже хотите.

public partial class CustomerEntities : DbContext
{
    protected CustomerEntities(string nameOrConnectionString):base(nameOrConnectionString)
    {         
    }
}

public class ReadonlyCustomerEntities : CustomerEntities
{
    public ReadonlyCustomerEntities ()
        : base("name=ReadonlyCustomerEntities")
    {          
    }

    public override int SaveChanges()
    {
        // Throw if they try to call this
        throw new InvalidOperationException("This context is read-only.");
    }
}