Сохранить одинаковые изменения в нескольких базах данных с Entity Framework - PullRequest
0 голосов
/ 05 февраля 2019

У меня есть 3 базы данных Oracle;производство, тестирование, разработка.По большей части они все идентичны.В моем приложении я хотел бы, чтобы изменения были применены к нескольким базам данных.Например:

    using (var context = new Context())
    {
        context.People.Add(new Person { name = "sean" });
        context.SaveChanges();
    }

Затем я попытался переопределить метод SaveChanges и сохранить в несколько баз данных, выполнив следующее:

    public void SaveChanges(int auditPersonNumber)
    {
        OracleCredentials.Default.Server = "VDev";
        base.SaveChanges();

        OracleCredentials.Default.Server = "VTest";
        base.SaveChanges();

        OracleCredentials.Default.Server = "VProd";
        base.SaveChanges();
    }

Это не сработало, но должно объяснить, что я пытаюсьдостичь.

Ответы [ 2 ]

0 голосов
/ 13 февраля 2019

Мне удалось найти решение благодаря помощи Sangman.

public class Context : Shared.Data.Context
{
    new public void SaveChanges(int auditPersonNumber)
    {
        var errors = string.Empty;
        var testConnectionString = "ConnectionString";
        var developmentConnectionString = "ConnectionString";

        //Save to test database
        if (SecurityMaintenanceUser.ApplyToTest)
            errors = ApplyToDatabase(testConnectionString, auditPersonNumber, "Test");

        if (!string.IsNullOrWhiteSpace(errors))
            errors += "\n\n";

        //Save to development database
        if (SecurityMaintenanceUser.ApplyToDevelopment)
            errors += ApplyToDatabase(developmentConnectionString, auditPersonNumber, "Development");

        if (!string.IsNullOrWhiteSpace(errors))
            MessageBox.Show(errors, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

        //Save to production database
        base.SaveChanges(auditPersonNumber);
    }

    private string ApplyToDatabase(string connectionString, int auditPersonNumber, string server)
    {
        try
        {
            using (var context = new Context(connectionString))
            {
                context.Configuration.ValidateOnSaveEnabled = false;

                foreach (var entry in ChangeTracker.Entries())
                {
                    var dataSet = context.Set(entry.Entity.GetType());

                    if (entry.State == EntityState.Added)
                    {
                        dataSet.Add(entry.Entity);
                    }
                    else if (entry.State == EntityState.Deleted)
                    {
                        var contextEntity = dataSet.Find(GetPrimaryKeyValues(entry));
                        context.DeleteEntity(contextEntity, auditPersonNumber);
                    }
                    else if (entry.State == EntityState.Modified)
                    {
                        var contextEntity = dataSet.Find(GetPrimaryKeyValues(entry));
                        context.Entry(CopyProperties(entry.Entity, contextEntity)).State = EntityState.Modified;
                    }
                }

                context.SaveChanges(auditPersonNumber);
                return string.Empty;
            }
        }
        catch (Exception e)
        {
            return $"Failed to apply database changes to {server}.\n{e.GetFullMessage()}";
        }
    }

    private object CopyProperties(object source, object destination)
    {
        if (source == null || destination == null)
            throw new Exception("Source or/and Destination Objects are null");

        var typeDest = destination.GetType();
        var typeSrc = source.GetType();

        foreach (var srcProp in typeSrc.GetProperties())
        {
            if (srcProp.Name == "Type" || srcProp.Name == "AuthenticationLog")
                continue;

            //This blocks any complex objects attached to the entity, will need to be changed for your application
            if (srcProp.PropertyType.FullName.Contains("Library.Shared"))
                continue;

            if (!srcProp.CanRead)
                continue;

            var targetProperty = typeDest.GetProperty(srcProp.Name);

            if (targetProperty == null)
                continue;

            if (!targetProperty.CanWrite)
                continue;

            if (targetProperty.GetSetMethod(true)?.IsPrivate == true)
                continue;

            if ((targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) != 0)
                continue;

            if (!targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType))
                continue;

            targetProperty.SetValue(destination, srcProp.GetValue(source, null), null);
        }

        return destination;
    }

    private object GetPrimaryKeyValues(DbEntityEntry entry)
    {
        var objectStateEntry = ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.GetObjectStateEntry(entry.Entity);
        return objectStateEntry.EntityKey.EntityKeyValues[0].Value;
    }

    public static string GetFullMessage(this Exception ex)
    {
        return ex.InnerException == null ? ex.Message : $"{ex.Message}\n{ex.InnerException.GetFullMessage()}";
    }

    public static string Replace(this string source, string oldString, string newString, StringComparison comp)
    {
        int index = source.IndexOf(oldString, comp);

        if (index >= 0)
        {
            source = source.Remove(index, oldString.Length);
            source = source.Insert(index, newString);
        }

        if (source.IndexOf(oldString, comp) != -1)
            source = Replace(source, oldString, newString, comp);

        return source;
    }
}
0 голосов
/ 06 февраля 2019

Я еще не использовал EntityFramework для базы данных Oracle, но он должен быть похож на соединение с SQL Server, так как имя базы данных указывается через ConnectionString.В вашем проекте должен быть файл конфигурации (web.config, app.config или, если это приложение .NET Core, это может быть файл appsettings.json) с этой строкой ConnectionString.

Например:

<add name="YourConnectionString" providerName="YourOracleProviderName" connectionString="User Id=test;Password=testpassword;Data Source=eftest" />

Базовый конструктор DbContext принимает строковый аргумент, который указывает, какую ConnectionString он должен использовать и, следовательно, к какой базе данных подключаться.Если вы заглянете в свой класс контекста, конструктор по умолчанию должен вызвать базовый конструктор с этим аргументом.

public YourDbContext() : base("YourConnectionString") {}

Чтобы сохранить в нескольких базах данных, вам нужно будет работать с разными экземплярами DbContext, каждый с разнымиАргумент ConnectionString.Итак, ваша конфигурация должна будет перечислить несколько разных строк подключения для каждого Db, и вы, вероятно, захотите, чтобы ваш класс DbContext также разрешил аргумент в своем конструкторе.

Возможно, реализация метода SaveChanges могла бы создать экземпляр другогоDbContexts вам нужно использовать:

    public void SaveChanges(int auditPersonNumber)
    {
        using (var context = new Context("OtherConnectionString1"))
        {
            // apply same changes
            context.SaveChanges();
        }

        using (var context = new Context("OtherConnectionString2"))
        {
            // apply same changes
            context.SaveChanges();
        }

        base.SaveChanges();
    }

Что касается применения тех же самых изменений, я ожидаю, что вы можете прочитать их из DbContext ChangeTracker.Здесь есть объяснение того, как использовать EF Core, но в более ранних версиях оно аналогично: http://www.entityframeworktutorial.net/efcore/changetracker-in-ef-core.aspx

Также имейте в виду, что вызов SaveChanges для OtherConnectionString1 может быть успешным, в то время как другие могут не работать, поэтому данные могут быть несовместимыми в вашем файле.разные базы данных.Возможно, вам придется изучить использование транзакций в нескольких базах данных, но я сам этого еще не сделал.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...