【问题标题】:Live Transfer of data from one provider to another in Entity Framework在实体框架中将数据从一个提供者实时传输到另一个提供者
【发布时间】:2021-01-31 18:55:25
【问题描述】:

如果已经有人问过这个问题,我深表歉意,我正在努力寻找我试图找出的术语,因为它与实体框架中的功能相冲突。

我想做什么:

我想创建一个应用程序,在设置时让用户使用 1 个数据库作为“试用”/“启动”数据库,即非生产数据库。这将允许用户试用应用程序,但不会有备份等。这绝不会是“生产”数据库。例如,这可能是 SQLite。

当用户准备好后,他们可以单击“转换为生产”(或类似的),并为其指定新数据库机器/数据库的目标。这将被视为“生产”环境。这可能是 MySQL、SQLServer 之类的东西,或者......这些天 EF 连接的任何其他东西......

问题:

EF 是否支持这种类型的实时迁移/数据传输?它是否需要另一个应用程序,您可以在其中配置 EF 源和 EF 目标,然后运行将数据源转换/播种/填充到另一个数据源的过程?

为什么我在这里问:

我试图搜索有关此主题的内容,但转移/迁移会带来完全不相关的主题,因此非常感谢任何帮助。

【问题讨论】:

    标签: c# entity-framework production-environment


    【解决方案1】:

    根据您的描述,我认为没有任何现成的东西可以支持这一点。您可以将 DbContext 映射到任一数据库,然后从评估 DbContext 获取和分离实体并将它们附加到生产数据库。

    对于一个相对简单的模式/对象图,这将是相当直接的实现。

    ICollection<Customer> customers = new List<Customer>();
    
    using(var context = new AppDbContext(evalConnectionString))
    {
       customers = context.Customers.AsNoTracking().ToList();
    }
    
    using(var context = new AppDbContext(productionConnectionString))
    { // Assuming an empty database...
        context.Customers.AddRange(customers);
    }
    

    虽然对于更复杂的模型,这可能需要一些工作,尤其是在处理现有查找/引用之类的事情时。如果您想将可能共享相同引用的对象移动到另一个对象,您需要在目标 DbContext 中查询现有亲属并在保存“父”实体之前替换它们。

    ICollection<Order> orders = new List<Order>();
    
    using(var context = new AppDbContext(evalConnectionString))
    {
       orders = context.Orders
           .Include(x => x.Customer)
           .AsNoTracking()
           .ToList();
    }
    
    using(var context = new AppDbContext(productionConnectionString))
    { 
        var customerIds = orders.Select(x => x.Customer.CustomerId)
            .Distinct().ToList();
        var existingCustomers = context.Customers
            .Where(x => customerIds.Contains(x.CustomerId))
            .ToList();
        
        foreach(var order in orders)
        {  // Assuming all customers were loaded
           var existingCustomer = existingCustomers.SingleOrDefault(x => x.CustomerId == order.Customer.CustomerId);
           if(existingCustomer != null)
               order.Customer = existingCustomer;
           else
               existingCustomers.Add(order.Customer);
    
           context.Orders.Add(order);
        }
    }
    

    这是一个非常简单的示例,概述了如何处理可能插入数据的场景,其中引用可能存在或不存在于目标 DbContext 中。如果我们要跨订单复制并希望与他们各自的客户打交道,我们首先需要检查是否存在任何跟踪的客户参考,并使用该参考来避免插入重复的行或引发异常。

    通常从一个 DbContext 加载订单和相关引用应确保引用同一客户实体的多个订单将共享同一实体引用。但是,要使用我们可以通过 AsNoTracking() 与新 DbContext 关联的分离实体,对同一记录的分离引用将是相同的引用,因此我们需要小心处理这些。

    例如,同一客户有 2 个订单:

    var ordersA = context.Orders.Include(x => x.Customer).ToList();
    Assert.AreSame(orders[0].Customer, orders[1].Customer); // Passes
    
    
    var ordersB = context.Orders.Include(x => x.Customer).AsNoTracking().ToList();
    Assert.AreSame(orders[0].Customer, orders[1].Customer); // Fails 
    

    即使在第二个示例中,两者都是针对同一客户的。每个都有一个具有相同 ID 的 Customer 引用,但有 2 个不同的引用,因为 DbContext 不跟踪所使用的引用。具有分离实体和努力提高性能等的几个“陷阱”之一。使用跟踪引用并不理想,因为这些实体仍会认为它们与另一个 DbContext 相关联。我们可以分离它们,但这意味着深入对象图并分离所有引用。 (可行,但与单独加载它们相比比较麻烦)

    当可能批量迁移数据(定期处理 DbContext 以避免较大数据量的性能缺陷)或随着时间的推移同步数据时,它也会变得复杂。通常建议首先检查目标 DbContext 以查找匹配记录并使用这些记录来避免插入重复数据。 (或抛出异常)

    如此简单的数据模型,这相当简单。对于更复杂的数据,需要带来更多数据以及数据之间的更多关系,情况会更加复杂。对于那些系统,我可能会考虑生成数据库到数据库的迁移,例如从源数据库中的数据为所需的目标数据库创建 INSERT 语句。只需按关系顺序插入数据以符合数据约束即可。 (使用工具或滚动您自己的脚本生成)

    【讨论】:

    • 哇,感谢您的深入回答。我只是真正将其视为我将如何处理的问题,而根本没有解决包含关系数据的问题。我想有人可以把它放在一个单独的应用程序/工具中,带有源和目标数据库上下文。然后给定应用程序的结构,正确传输数据,例如:库存、支付类型/历史、订单,客户(使用您的示例).. 但需要进行大量测试以确保翻译正确!
    猜你喜欢
    • 1970-01-01
    • 2019-11-09
    • 2013-11-03
    • 1970-01-01
    • 1970-01-01
    • 2020-03-22
    • 2020-12-25
    • 2021-07-30
    • 1970-01-01
    相关资源
    最近更新 更多