c# – 在Entity Framework 5中重写SaveChanges首先复制旧的旧版本的行为

前端之家收集整理的这篇文章主要介绍了c# – 在Entity Framework 5中重写SaveChanges首先复制旧的旧版本的行为前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我们公司提供一套操作数据库中的数据的各种应用程序.每个应用程序都有其特定的业务逻辑,但所有应用程序都共享一个常见的业务规则子集.常见的东西在一堆使用“经典ADO”(通常称为存储过程,有时使用动态sql)的C中编写的遗留COM DLL中.大多数这些DLL具有基于 XML方法(更不用说基于专有格式的方法!)来创建,编辑,删除和检索对象,还有一些额外的操作,例如快速复制和转换许多实体的方法.

中间件DLL现在很老了,我们的应用程序开发人员需要一个可以很容易地被C#应用程序使用的新的面向对象(不是面向对象的)中间件.
公司的许多人说我们应该忘记旧的范式,并转移到新的酷的东西,如实体框架.他们对POCO的简单性感兴趣,他们希望使用LINQ检索数据(DLL的基于Xml的查询方法不是那么容易使用,永远不会像LINQ一样灵活).

所以我试图为一个简化的场景创建一个模型(真实场景要复杂得多,在这里我将发布简化场景的简化子集).我使用Visual Studio 2010,实体框架5代码第一,sql Server 2008 R2.
如果我犯了愚蠢的错误,请放心,我是Entity Framework的新手.
由于我有很多不同的疑问,我会把它们分开的线程.
这是第一个.传统的XML方法有这样的签名:

bool Edit(string xmlstring,out string errorMessage)

使用如下格式:

<ORDER>
    <ID>234</ID>
    <NAME>SuperFastCar</NAME>
    <QUANTITY>3</QUANTITY>
    <LABEL>abc</LABEL>
</ORDER>

编辑方法实现了以下业务逻辑:当数量更改时,必须对所有具有相同标签的订单应用“自动缩放”.
例如.有三个订单:OrderA的数量= 3,label = X. OrderB的数量= 4,label = X. OrderC的数量= 5,label = Y.
调用Edit方法为OrderA提供新的数量= 6,即将OrderA的数量翻倍.那么根据业务逻辑,OrderB的数量必须自动加倍,并且必须成为8,因为OrderB和OrderA具有相同的标签. OrderC不能更改,因为它有一个不同的标签.

如何用POCO类和实体框架复制?这是一个问题,因为旧的编辑方法一次只能更改一个订单
调用SaveChanges时,实体框架可以更改很多订单.此外,对SaveChanges的单一调用也可以创建新的订单.
临时假设,仅供此次测试:1)如果许多订单数量同时更改,并且缩放因子对于所有订单量都不相同,则不会缩放; 2)新添加的订单即使具有相同的订单标签也不会自动缩放.

我试图通过覆盖SaveChanges来实现它.

POCO类:

using System;

namespace MockOrders
{
    public class Order
    {
        public Int64 Id { get; set; }
        public string Name { get; set; }
        public string Label { get; set; }
        public decimal Quantity { get; set; }
    }
}

迁移文件(创建索引):

namespace MockOrders.Migrations
{
    using System;
    using System.Data.Entity.Migrations;

    public partial class UniqueIndexes : DbMigration
    {
        public override void Up()
        {
            CreateIndex("dbo.Orders","Name",true /* unique */,"myIndex1_Order_Name_Unique");
            CreateIndex("dbo.Orders","Label",false /* NOT unique */,"myIndex2_Order_Label");
        }

        public override void Down()
        {
            DropIndex("dbo.Orders","myIndex2_Order_Label");
            DropIndex("dbo.Orders","myIndex1_Order_Name_Unique");
        }
    }
}

的DbContext:

using System;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;
using System.Linq;

namespace MockOrders
{
    public class MyContext : DbContext
    {
        public MyContext() : base(GenerateConnection())
        {
        }

        private static string GenerateConnection()
        {
            var sqlBuilder = new System.Data.sqlClient.sqlConnectionStringBuilder();
            sqlBuilder.DataSource = @"localhost\aaaaaa";
            sqlBuilder.InitialCatalog = "aaaaaa";
            sqlBuilder.UserID = "aaaaa";
            sqlBuilder.Password = "aaaaaaaaa!";
            return sqlBuilder.ToString();
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new OrderConfig());
        }

        public override int SaveChanges()
        {
            ChangeTracker.DetectChanges();

            var groupByLabel = from changedEntity in ChangeTracker.Entries<Order>() 
                        where changedEntity.State == System.Data.EntityState.Modified
                                && changedEntity.Property(o => o.Quantity).IsModified
                                && changedEntity.Property(o => o.Quantity).OriginalValue != 0
                                && !String.IsNullOrEmpty(changedEntity.Property(o => o.Label).CurrentValue)
                        group changedEntity by changedEntity.Property(o => o.Label).CurrentValue into x
                        select new { Label = x.Key,List = x};

            foreach (var labeledGroup in groupByLabel)
            {
                var withScalingFactor = from changedEntity in labeledGroup.List 
                    select new 
                    { 
                        ChangedEntity = changedEntity,ScalingFactor = changedEntity.Property(o => o.Quantity).CurrentValue / changedEntity.Property(o => o.Quantity).OriginalValue 
                    };

                var groupByScalingFactor = from t in withScalingFactor 
                                           group t by t.ScalingFactor into g select g;

                // if there are too many scaling factors for this label,skip automatic scaling
                if (groupByScalingFactor.Count() == 1)
                {
                    decimal scalingFactor = groupByScalingFactor.First().Key;
                    if (scalingFactor != 1)
                    {
                        var query = from oo in this.AllTheOrders where oo.Label == labeledGroup.Label select oo;

                        foreach (Order ord in query)
                        {
                            if (this.Entry(ord).State != System.Data.EntityState.Modified
                                && this.Entry(ord).State != System.Data.EntityState.Added)
                            {
                                ord.Quantity = ord.Quantity * scalingFactor;
                            }
                        }
                    }
                }

            }

            return base.SaveChanges();

        }

        public DbSet<Order> AllTheOrders { get; set; }
    }

    class OrderConfig : EntityTypeConfiguration<Order>
    {
        public OrderConfig()
        {
            Property(o => o.Name).HasMaxLength(200).Isrequired();
            Property(o => o.Label).HasMaxLength(400);
        }
    }
}

它似乎工作(当然是阻止bug),但这只是一个例子:只有一个类:一个真正的生产应用程序可能有数百个类!
恐怕在一个真实的情况下,由于有很多限制和业务逻辑,SaveChanges的覆盖可能很快变得很长,混乱和容易出错.
一些同事也关心表演.在我们的遗留DLL中,很多业务逻辑(如“自动”操作)都存在于存储过程中,一些同事担心基于SaveChanges的方法可能会引入太多的往返行为并阻碍性能.
在SaveChanges的覆盖中,我们也可以调用存储过程,但事务完整性呢?如果我更改数据库怎么办?
之前我调用“base.SaveChanges()”和“base.SaveChanges()”失败?

有不同的方法吗?我错过了什么吗?

非常感谢你!

德梅特里奥

附:顺便说一下,覆盖SaveChanges并注册到“SavingChanges”事件之间有什么不同吗?我阅读了这份文件,但并没有解释是否有区别:
http://msdn.microsoft.com/en-us/library/cc716714(v=vs.100).aspx

这篇文章
Entity Framework SaveChanges – Customize Behavior?

说“当覆盖SaveChanges时,您可以在调用base.SaveChanges之前和之后放置自定义逻辑.但还有其他注意事项/优点/缺点?

解决方法

我会说这个逻辑属于你的MockOrders.Order类,在一个使用你的Order类(例如BusinessLogic.Order)或Label类的更高层的类中.听起来像你的标签作为一个加入属性,所以,不知道的细节,我会说拉出来,使它成为一个自己的实体,这将给你导航属性,所以你可以更自然地访问所有订单与相同的标签.

如果修改数据库以使规范化标签不是一个参与者,那么建立一个视图,并将其带入您的实体模型中.

猜你在找的C#相关文章