Entity Framework 6 Recipes 2nd Edition(9-5)译->删除一个断开的实体

9-5. 删除一个断开的实体

问题

我们要把一个把WCF上取回的对象做上删除的标志.

解决方案

假设我们有如Figure 9-5所示实体的支付与票据的模型.

 

Figure 9-5. 一个支付与票据的模型

我们的模型展示了支付记录与票据的关系。在应用程序中,我们用客户端与用WC封装EF数据访问交互

. 在我们的例子中,我们要用WCF服务删除Payment (支付)实体。为尽可能简单,我们创建一个WCF服务库并且定义模型:

1.右击解决方案,选择新项目,选择WCF ➤WCF服务库,并命名为:Recipe5

2. 右击Recipe5 项目➤添加➤新建项,选择“数据” ➤ ADO.NET 实体数据模型. 根据向导,添加 Invoice 和 Payment 表. 简单起见,我们删除了Invoice实体的导航属性Payments(在Invoice实体的Payments属性上右击,选择“从模型删除”)右击Payment 实体的TimeStamp 属性, 选择“属性”, 设置“并发模式”为Fixed. 这样会使TimeStamp做为并发控制,当删除或更新实体时,它的值将作为SQL语句的Where条件的一部分,3. 用Listing 9-27里的代码,改变IService1.cs文件里的服务定义.

Listing 9-27. The Service Contract for Our WCF Service

    [ServiceContract]

    public interface IService1

    {

        [OperationContract]

        Payment InsertPayment();

 

        [OperationContract]

        void DeletePayment(Payment payment);

}

4. 在Service1.cs文件中, 实现服务,如Listing 9-28所示.

Listing 9-28. The Implementation of Our Service Contract

  public class Service1 : IService1

    {

 

        public Payment InsertPayment()

        {

            using (var context = new EFRecipesEntities())

            {

                //删除之前的测试数据

                context.Database.ExecuteSqlCommand("delete from chapter9.payment");

                context.Database.ExecuteSqlCommand("delete from chapter9.invoice");

                var payment = new Payment

                {

                    Amount = 99.95m,

                    Invoice = new Invoice { Description = "Auto Repair" }

                };

                context.Payments.Add(payment);

                context.SaveChanges();

                return payment;

            }

        }

 

        public void DeletePayment(Payment payment)

        {

            using (var context=new EFRecipesEntities())

            {

                context.Entry(payment).State = EntityState.Deleted;

                context.SaveChanges();

            }

        }

}

5. 为测试服务, 需要一个客户端. 在解决方案里添加一个新的控制台应用程序项目

. 客户端代码如Listing 9-29所示. 在WCF上右击,调试➤启动新实例,再在控制台项目上右击➤添加➤服务引用,然后添加对WCF的引用.

Listing 9-29. A Simple Console Application to Test Our WCF Service

    class Program

    {

        static void Main(string[] args)

        {

            var client = new ServiceReference1.Service1Client();

            var payment = client.InsertPayment();

            client.DeletePayment(payment);

        }

}

如果你在Main()方法首行代码前设置断点,然后调试,“逐语句”执行,可以看到WCF服务的执行

它是如何工作的

在本小节, 我们演示了客户端调用服务对断开连接的实体进行操作

.在客户端,我们用InsertPayment() 方法向数据库插入一个新的  payment. 该方法返回被插入的

payment. 当然  payment 返回给客户端后就从DbContext中断开连接.实际上DbContext可以在不同的进程里,甚至于在不同计算机上,

我们用DeletePayment() 方法来把数据库中的Payment删除  . 这个方法中 (见Listing 9-28), 我们调用DbContext 的Entry() 方法,并传递一个 Payment参数. 然后把Payment实体的属性设置为EntityState.Deleted,接着SaveChanges() 方法会生成一条删除的SQL语句,并把数据从数据库中删除. 因为我们用了外键关联, 并发属性TimeStamp是必需的,EF会在SQL的Where子句中使用到。

用这种方式解决迸发. 会遇到一个问题,当你的POCO类有一个或更多复合型属性时,因为复合类型,在EF里是被重视的,不能为null,简单和解决办法是:为复合类型创建一个虚拟的实例。如果你让复合类型为null,SaveChanges()方法将会抛出一个异常。

如果在多对1和0..1中使用一个独立关联,EF要求它们的实体键具有正确的值,以为修改和删除生成Where子句. 在我们例子里,如果在Invoice和Payment中有一个独立关联,我们需要给Invoice的InvoiceId导航设置正确的值,来关联Invoice实例.这样Where子句里将会包含PaymentId, TimeStamp, 和InvoiceId.

===================================================================

■■注意:当用EF实例一个N层架构时,应慎重地考虑是否用分配外键来关联相关的实体,独立关联比较难实现,并会使你的代码变得复杂. 对此EF团队的Arthur Vickers 发布的whats-the-deal-with-mapping-foreign-keys-using-the-entity-framework 博客,有个很好的解释.当然也欢迎你细读他的EF的其它文章.

=======================================================================

如果实体对象包含多个独立关联,设置它们就是件乏味的事,你可以简单地从数据库重新取回然后再删除,这样会使你代码简单些,但是当你从数据库重新取回,EF将重写这些实体间关系

, 当然你可以有NoTracking 选项关闭context 跟踪来解决.

如果本小节我们用独立关联的方式,为已经加载了Payment 实体做删除标志时,EF将会对Payment和相关联的Invoice做上删除标志。

同样地,结果的SQL名句的Where子句包含PaymentId, TimeStamp, and InvoiceId列。

在独立关联在删除实体的另一个选择是:预先加载相关联的实体,然后将整个对象图传给WCF或是Web API来进行删除。就我们的实例,我们可以预先加载Invoice和相关的Payment实体。如果我们要删除Payment实体,我们需要回传包含Invoice实体的整个对象给服务。不过这样会消费更多带宽有更多序列处处理的时间,所以它可能带来的好处还比不上消耗。

附:创建示例用到的数据库的脚本文件

posted @ 2016-01-19 14:00  kid1412  阅读(399)  评论(0编辑  收藏  举报