首页 > 其他分享 >N层研习中的测试代码01

N层研习中的测试代码01

时间:2023-07-02 11:03:20浏览次数:53  
标签:01 Writer db 更新 WriteLine 测试代码 OriginalCustomer 主键 研习


这是前文《N层研习记录01:试图通过Boolean参数控制并发冲突的检查方式(LINQ to SQL)》用到的测试代码。只是包含了其中最重要的部分,如果要想获取完整的代码,可以通过以下地址进行下载:
下载地址2:http://u.115.com/file/f26716bcc2以上地址如果均不能下载,请留言通知我!

测试代码的服务端使用的是WCF,客户端是一个控制台应用程序。其中也包含了测试中的一些记录,可参看代码中的注释部分。
下面这段代码是服务实现的主要代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using NLayer.Contracts;
using NLayer.Model;
using System.Data.Linq;
using System.IO;

namespace NLayer.Services
{
    public class NorthwindServices : INorthwindService
    {
        /// <summary>
        /// 获取系统中所有的客户数据。
        /// </summary>
        /// <returns></returns>
        public IEnumerable<Customer> RetrieveAllCustomers()
        {
            NorthwindExDataContext db = new NorthwindExDataContext();
            StreamWriter Writer = File.AppendText(@"C:/LINQ/NLayer.log");
            db.Log = Writer;

            Writer.WriteLine("-------------------------------------------------");
            Writer.WriteLine("NorthwindServices.RetrieveAllCustomers()");
            Writer.WriteLine();

            var AllCustomers =
                    from CustomerObject in db.Customers
                    select CustomerObject;

            Writer.Close();

            return AllCustomers.AsEnumerable();
        }

        /// <summary>
        /// 获取指定编号的客户数据。
        /// </summary>
        /// <param name="CustomerID"></param>
        /// <returns></returns>
        public Customer RetrieveCustomer(string CustomerID)
        {
            NorthwindExDataContext db = new NorthwindExDataContext();
            StreamWriter Writer = File.AppendText(@"C:/LINQ/NLayer.log");
            db.Log = Writer;

            Writer.WriteLine("-------------------------------------------------");
            Writer.WriteLine("NorthwindServices.RetrieveCustomer()");
            Writer.WriteLine();

            var QueryCustomer =
                from CustomerObject in db.Customers
                where CustomerObject.CustomerID == CustomerID
                select CustomerObject;

            var Result = QueryCustomer.First();
            Writer.Close();

            return Result;
        }
        
        /// <summary>
        /// 删除指定的客户对象。
        /// </summary>
        /// <param name="DeletedCustomer"></param>
        public void DeleteCustomer(Customer DeletedCustomer)
        {
            NorthwindExDataContext db = new NorthwindExDataContext();
            StreamWriter Writer = File.AppendText(@"C:/LINQ/NLayer.log");
            db.Log = Writer;

            Writer.WriteLine("-------------------------------------------------");
            Writer.WriteLine("NorthwindServices.DeleteCustomer()");
            Writer.WriteLine();

            try
            {
                db.Customers.Attach(DeletedCustomer, false);

                // 如果根据MSDN文档的说法,这里设置为false。
                // 不过,到现在我可以说,这个地方设置一个什么样的参数值,
                // 都不是用来控制LINQ to SQL采用哪种方式执行删除和更新操作的。

                // 这个方法删除客户也不是基于原始值的方式,而是基于时间戳和主键方式删除的。
                // 另外此方法将会引发异常,因为可能所指定的客户还包含有一些订单的。

                db.Customers.DeleteOnSubmit(DeletedCustomer);
                db.SubmitChanges();
            }
            catch (Exception ex)
            {
                Writer.WriteLine(ex.Message);
            }
            finally
            {
                Writer.Close();
            }
        }

        /// <summary>
        /// 试图使用基于时间戳的方式更新指定的客户对象。
        /// </summary>
        /// <param name="CustomerObject"></param>
        public void UpdateCustomerByRowVersion(Customer CustomerObject)
        {
            NorthwindExDataContext db = new NorthwindExDataContext();
            StreamWriter Writer = File.AppendText(@"C:/LINQ/NLayer.log");
            db.Log = Writer;

            Writer.WriteLine("-------------------------------------------------");
            Writer.WriteLine("NorthwindServices.UpdateCustomerByRowVersion()");
            Writer.WriteLine();

            try
            {
                db.Customers.Attach(CustomerObject, true);
                
                // 如果这里设置为false的话,那等于就是告诉给LINQ to SQL这个对象是没有被修改的,
                // 而且在后面的代码中也没有修改过这个对象,
                // 这样一来在当前数据上下文跟踪的对象中就没有一个对象被修改了,
                // 既然如此也就没有必要生成相应的UPDATE的SQL语句了。
                // 怪不得测试中始终无法看到相应的SQL命令。哎!

                // 不过这里虽然设置为true了,实际所生成的更新命令却不是根据时间戳来更新的,
                // 而是根据客户的编号来更新的,这是为什么呢?可能是因为我把实体类的版本列成员给修改为普通成员了。
                // 到底什么情况下会使用基于时间戳的方式更新数据呢?

                // 如果实体类有主键列却没有版本列的话,就算此时数据库中仍然具有版本列,
                // 也同样不会使用基于时间戳的方式,而是仅仅基于主键的方式。
                // 如果实体类连主键也没有呢?或者实体类的版本列并非对应于数据库中的版本列呢?

                // 如果实体类一个主键也没有设置的话,无法在其上执行Create、Update或Delete操作。
                // 如果此时要是设置一个版本列,但没有主键列,又会怎样呢?
                // 呵呵,没怎么样,有版本列又能怎样,只要没有主键列,就是不行。
                // 那如果主键列属性不对应于数据表的主键列呢?

                // 如果在实体类中设置的主键属性不对应于数据表中的主键列,同样可以更新数据。
                // 只是是基于实体类设置的主键属性和版本列来更新的,而并非是数据表中的主键。

                // 通过以上的测试表明:所谓的基于主键和版本列更新数据,
                // 其实是基于实体类的主键列属性和版本列属性,而并非数据表中的主键和版本列。
                // 因此,只有正确的映射主键列和版本列才可以确保万无一失。
                // 另外,我想删除操作应该也是这么一回事吧!

                db.SubmitChanges();
            }
            catch (Exception ex)
            {
                Writer.WriteLine(ex.Message);
            }
            finally
            {
                Writer.Close();
            }
        }

        /// <summary>
        /// 采用基于修改部分属性值的方式更新数据。
        /// 
        /// </summary>
        /// <param name="OriginalCustomer"></param>
        /// <param name="ContactName"></param>
        /// <param name="ContactTitle"></param>
        public void UpdateCustomerContact(Customer OriginalCustomer, 
            string ContactName, 
            string ContactTitle)
        {
            NorthwindExDataContext db = new NorthwindExDataContext();
            StreamWriter Writer = File.AppendText(@"C:/LINQ/NLayer.log");
            db.Log = Writer;

            Writer.WriteLine("-------------------------------------------------");
            Writer.WriteLine("NorthwindServices.UpdateCustomerContact()");
            Writer.WriteLine();

            try
            {
                db.Customers.Attach(OriginalCustomer, false);
                OriginalCustomer.ContactName = ContactName;
                OriginalCustomer.ContactTitle = ContactTitle;
                db.SubmitChanges();

                //

缺点:此方式更新数据的灵活性不高,不过也可以为每个列属性都定义一个参数。 // 呵呵,如果这样做的话,还不如使用下面那种基于完整实体的方式更新数据呢。 // 经过测试此代码实际上也是基于时间戳和主键的方式更新数据的。 // 实体类不能没有主键列属性,如果没有的话就无法执行增加、删除和更新的操作。 // 虽然可以没有版本列属性,但即使没有这个属性,那也是基于主键方式进行数据的更新, // 而并非那种基于原始值的方式呀。 // 不过,如果主键属性,而没有版本列属性,并且主键属性不对应于数据表中的主键列, // 那又会生成怎样的SQL呢? // 真是太恐怖了。仍然是采用基于主键列属性的方式来更新数据的。 // 如果所设置的主键列属性映射到数据表中的那个列可以包含重复值的话, // 那么更新数据的时候可能就会同时更新若干行。 // 比如,我测试时是将【城市】列属性设置为主键列的, // 结果竟然把居住在【柏林】的所有客户的姓名和联系方式都更新成为一样的了。 // 哎!再一次体会到正确映射主键列属性的重要性! // 如果映射错误的话,会死的很难看的。 // 另外,通过这些测试充分说明:LINQ to SQL在生成SQL命令的时候, // 是根据对象模型中的信息来设置的,而并非是根据数据库设置的。 // 这也是合情合理的,因此最好使对象模型正确的映射到数据库,否则可能会出现意想不到的效果。

}
            catch (Exception ex)
            {
                Writer.WriteLine(ex.Message);
            }
            finally
            {
                Writer.Close();
            }
        }

        /// <summary>
        /// 基于完整实体的方式更新数据。
        /// </summary>
        /// <param name="OriginalCustomer"></param>
        /// <param name="NewCustomer"></param>
        public void UpdateCustomer(Customer OriginalCustomer, 
            Customer NewCustomer)
        {
            NorthwindExDataContext db = new NorthwindExDataContext();
            StreamWriter Writer = File.AppendText(@"C:/LINQ/NLayer.log");
            db.Log = Writer;

            Writer.WriteLine("-------------------------------------------------");
            Writer.WriteLine("NorthwindServices.UpdateCustomer()");
            Writer.WriteLine();

            try
            {
                db.Customers.Attach(NewCustomer, OriginalCustomer);
                db.SubmitChanges();
            }
            catch (Exception ex)
            {
                Writer.WriteLine(ex.Message);

                //

更新一直出现异常情况。可是初开始更新的时候就没有异常情况, // 现在终于明白是什么原因了。 // 原来我第一次更新数据的时候所使用的数据表及实体类是没有时间戳列的, // 而这里测试时使用的数据表有时间戳列,而且映射的实体类也有版本列。 // 光是这样并不会引发异常,引发异常的原因是我下意识的以为时间戳列是由数据库生成的, // 因此在创建新的对象是,并没有将原始对象的时间戳列的属性值赋值给新对象的时间戳列属性, // 这样一来原始对象和新建对象的版本属性的值当然也就不相同了。 // 我想,可能因为这样,在附加的时候就引发了异常。还是捕获异常好呀! // 接着,我就将原始对象的版本列属性值赋给新建对象的版本列属性,这次自然也就成功执行了。 // 另外经过这个测试我感觉:到底是使用基于时间戳的方式,还是基于原始值的方式更新数据, // 这个选择貌似完全是由LINQ to SQL来自动选择的。 // 我一开始就认为这种基于完整实体对象的方式更新数据应该是很自然的采用基于原始属性值的方式更新数据, // 但是根据捕获到的SQL语句来看,它确实基于时间戳和主键的方式更新数据的。 // 莫非我们真的无法控制到底该采用哪种方式更新数据吗? // 怎么到现在为止,所有的测试都是基于时间戳和主键列来执行的呢? // 如果将实体类的版本列成员取消为普通成员的话,那也是根据主键列来执行的, // 这样一来岂不是无法很好的控制并发冲突吗? // 到目前为止,至少可以断定的是,数据表最好还是添加一个版本列, // 同时在对象模型中也映射一个版本列属性,这样是最简单的控制并发冲突的做法。 // 可问题是,如何实现基于原始值的方式更新数据呢? } finally { Writer.Close(); } } } }
下面是客户端的测试代码,运行此代码时可能需要将部分代码注释掉,否则可能会产生编译时错误。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using NLayer.Client.NorthwindServiceReference;

namespace NLayer.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            // *************************************************
            // 删除数据:尝试基于时间戳的方式删除数据。
            // *************************************************
            NorthwindServicesClient Client = new NorthwindServicesClient();
            Customer CustomerObject = Client.RetrieveCustomer("ALFKI");
            Client.DeleteCustomer(CustomerObject);


            // *************************************************
            // 更新数据:尝试基于时间列的方式更新数据。
            // *************************************************
            NorthwindServicesClient Client = new NorthwindServicesClient();
            Customer CustomerObject = Client.RetrieveCustomer("ALFKI");
            CustomerObject.ContactName = CustomerObject.ContactName + "1";
            CustomerObject.ContactTitle = CustomerObject.ContactTitle + "1";
            Client.UpdateCustomerByRowVersion(CustomerObject);


            // *************************************************
            // 更新数据:尝试基于部分列属性修改的方式更新数据。
            // *************************************************
            NorthwindServicesClient Client = new NorthwindServicesClient();
            Customer CustomerObject = Client.RetrieveCustomer("ALFKI");
            Client.UpdateCustomerContact(CustomerObject,
                "Maria Anders",
                "Sales Representative");


            // *************************************************
            // 更新数据:尝试基于完整实体的方式更新数据。
            // *************************************************
            NorthwindServicesClient Client = new NorthwindServicesClient();
            Customer OriginalCustomer = Client.RetrieveCustomer("ALFKI");
            Customer NewCustomer = new Customer()
            {
                Address = OriginalCustomer.Address,
                City = OriginalCustomer.City,
                ContactName = OriginalCustomer.ContactName,
                ContactTitle = OriginalCustomer.ContactTitle,
                Country = OriginalCustomer.Country,
                CustomerID = OriginalCustomer.CustomerID,
                Fax = OriginalCustomer.Fax,
                Phone = OriginalCustomer.Phone,
                PostalCode = OriginalCustomer.PostalCode,
                Region = OriginalCustomer.Region,
                Version = OriginalCustomer.Version
            };
            NewCustomer.ContactName = NewCustomer.ContactName + "2";
            NewCustomer.ContactTitle = NewCustomer.ContactTitle + "2";
            Client.UpdateCustomer(OriginalCustomer, NewCustomer);
        }
    }
}

标签:01,Writer,db,更新,WriteLine,测试代码,OriginalCustomer,主键,研习
From: https://blog.51cto.com/u_3319687/6603753

相关文章

  • [MEF]第01篇 MEF使用入门
    一、演示概述此演示初步介绍了MEF的基本使用,包括对MEF中的Export、Import和Catalog做了初步的介绍,并通过一个具体的Demo来展示MEF是如何实现高内聚、低耦合和高扩展性的软件架构。演示中,针对于IBookService接口,有3个不同版本的实现,分别是ComputerBookServiceImp、HistoryBookSer......
  • [NOIP2001 提高组] 一元三次方程求解
    [NOIP2001提高组]一元三次方程求解题目描述有形如:\(ax^3+bx^2+cx+d=0\)这样的一个一元三次方程。给出该方程中各项的系数(\(a,b,c,d\)均为实数),并约定该方程存在三个不同实根(根的范围在\(-100\)至\(100\)之间),且根与根之差的绝对值\(\ge1\)。要求由小到大依......
  • 选读SQL经典实例笔记01_检索和排序
    1. 在WHERE子句中引用别名列1.1. 当表里的某些列没有被恰当命名的时候,这个技巧尤其有用1.2. sqlselectsalassalary,commascommissionfromempwheresalary<50001.3. 内嵌视图1.3.1.  sqlselect*from(selectsalassalary,commascommission......
  • 数据结构和算法-2023.07.01
    数据结构杂记回忆以前的一些零散的知识点杂谈......
  • Web安全-渗透测试-权限提升01
    权限提升权限提升对我们在深度渗透过程中起着重要作用,接下来我将介绍网站后台,漏洞,第三方,数据库,服务类,第三方接口,服务器系统几方面进行提权介绍参考1后台权限获得方式:爆破,注入猜解,弱口令等获取的帐号密码配合登录一般网站或应用后台只能操作应用的界面内容数据图片......
  • 名人名言_2023.07.01-
    日常学习名人名言,激励自己......
  • 2023-07-01 开摆
    CF671EOrganizingaRace考虑一组\([L,R]\)是否合法。最优的策略肯定是,从\(L\)开始往右走,每次发现油不够了就贪心在自己这里加油。最后把所有剩下的全加在\(R\)上。现在描述一下“油不够”的情况。设\(f_x\)表示从\(n\)走到\(x\)的油量。(可以发现,\(f\)可能有负数......
  • 暑假第二周(6/25~7/01)
    6/25 从今天起,我爸我妈要上班(明明是周日),我弟要上学(万恶且该死的调休政策),所以今天一个人在家(中午也没一个人回来)早上8点,在太阳光的催促下我睁开了双眼,妈妈应该是刚走没多久,弟弟是五点钟起的床,不到六点就到了学校,现在学生真苦,初一就这么紧张。我起来进行洗漱,肚子饿了,在厨房里找......
  • 2023-07-01:redis过期策略都有哪些?LRU 算法知道吗?
    2023-07-01:redis过期策略都有哪些?LRU算法知道吗?答案2023-07-01:缓存淘汰算法(过期策略)当Redis的内存超出物理内存限制时,内存中的数据就会频繁地与磁盘进行交换,这个过程叫做交换(swap)。由于交换的高开销,Redis的性能会急剧下降。对于访问频率较高的Redis实例来说,这样低效的存取效率......
  • 2023-07-01:redis过期策略都有哪些?LRU 算法知道吗?
    2023-07-01:redis过期策略都有哪些?LRU算法知道吗?答案2023-07-01:缓存淘汰算法(过期策略)当Redis的内存超出物理内存限制时,内存中的数据就会频繁地与磁盘进行交换,这个过程叫做交换(swap)。由于交换的高开销,Redis的性能会急剧下降。对于访问频率较高的Redis实例来说,这样低效的存取效率几乎......