`

LINQ技术

阅读更多

最近通过学习J2EE了解到J2EE的Hibernate类似于ASP.NET的LINQ   J2EE的Struts类似于ASP.NET MVC

从现在开始让我们来学习LINQ to SQL,其实在前面我们也提到了一些关于LINQ to SQL的知识:http://kendezhu.iteye.com/blog/757044 and http://kendezhu.iteye.com/blog/737526(LinqDataSource控件)。所以关于如何使用LINQ to SQL类创建数据库实体类(数据上下文类),表实体类,及将数据库中的存储过程函数映射为数据上下文类里的方法等这里就不说了。其中表实体类中的字段或属性代表数据库表中的列(字段),数据库实体类里的方法代表数据库里的存储过程或方法,表实体类之间的关联代表数据库里表之间的外键关系。而这一切代码都是在我们将表或存储过程,方法从资源管理器中拖到O/R设计器里保存后自动生成的(主要位于**.designer.cs里,该文件主要定义了数据上下文类及其内的存储过程或函数对应的方法及各个表的实体类等),是由LINQ代码生成工具SqlMetail.exe生成的(该工具位于C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin目录下)。一般一个网站只有一个数据库,所以只需用一个LINQ to SQL类,但如果有两个LINQ to SQL类,要注意它们中的表实体类的属性及数据库实体类的方法是否冲突。

 

LINQ学习相关:

http://www.cnblogs.com/kirinboy/archive/2010/01/20/linq-to-sql-update-1.html

http://www.cnblogs.com/chwkai/archive/2009/12/31/linq.html

http://www.tzwhx.com/newOperate/html/1/11/116/20507.html

http://www.cnblogs.com/silenus1986/archive/2009/06/15/1503598.html

http://www.cnblogs.com/yangtongnet/archive/2010/05/10/1731728.html

 

处理EntitySet<T>类型的结果   using System.Data.Linq;

http://msdn.microsoft.com/zh-cn/library/bb342388(VS.90).aspxl

 

首先从字面意思上来看EntitySet<T>是泛型实体集的意思,这里的实体也是指记录的实体(不是表的实体吗?这个下面会讲,而且这些记录集只是表的一部分满足条件的记录)。EntitySet<T>类型的属性是出现在当两个表之间有外键关系起主要作用(我也不知道怎么说,总之是A表有个a字段,然后B表有个b字段依赖于它,即当B表要添加记录时,b字段里的值必须在A表里a字段的值里出现过。在设计外键时,要实现删除A表里的a字段的某值时也同时删除B表里b字段里具有相同值的那些记录,需要将"钥匙"从b字段指向a字段)的表的实体类里(在*.designer.cs里起主要作用的表实体类里可以看到这种类型的字段及属性,当两表有外键关系时)。

数据上下文类可直接使用(因为位于同一命名空间),并且不用在其重载里填写数据库连接字符串,因为在数据库上下文类里已经在其构造函数里指明了(其构造函数还有好多重构)。在*.designer.cs内的数据上下文类里我们还可以看到类型是Table<T>的属性,通过这种属性我们可以得到相应表实体类的实例(其类型是Table<T>)。

            protected void Button1_Click(object sender, EventArgs e)

          {

           linqtosqlDataContext db = new linqtosqlDataContext();

 直接使用数据上下文类,并且不用在其重载里填写数据库连接字符串,也不用管连接的打开与关闭

        var result = from u in db.ComManager where u.ComManagerID == 3 select u.Name;

 通过数据上下文类实例db的ComManager属性我们可以得到数据库中ComManager表的实例,其类型是  Table<ComManager>,ComManager为表的实体类,其实前面讲的创建表实体类,不要以为该表的类型是ComManager类,其实从这里可以看出该表的类型其实是Table<ComManager>,就是类型是ComManager的实体的集合,那么这些实体是什么呢?很明显就是一条条记录(并且是该表内所有记录的集合,这一点与EntitySet<T>类型的集合不同)。所以偶认为ComManager类与其叫表的实体类不如叫记录的实体类。可以从智能提示中看到u的类型就是ComManager,所以u就代表ComManager表里的记录。

 查询的是ComManager表内ComManagerID等于3的记录的Name字段的值

            foreach (var item in result)

 LINQ to SQL的result为IQueryable<T>类型而非我们经常遇到的IEnumerable<T>类型

            {

                Label1.Text = item;

            }

 

           }

http://hi.baidu.com/grayworm/blog/item/8760d53d51e0cc0cbaa16794.html|荐

http://zhuqiangwei.0fees.net/showArticle.php?id=43

http://terryfeng.iteye.com/blog/516116

通过上面的一系列分析,不难发现EntitySet<T>类型的集合里的记录集是与当前表有外键关系的表里符合条件的记录集,现在我们来加入EntitySet<T>类型:

前提条件是Company表(公司表)与Employ表(招聘表)有外键关系且Company表为"我所谓的"起主要作用的表。这样我们到*.designer.cs里可以在Company类里看到有EntitySet<Empoly>类型的属性,这样我们就可以通过Company类的实例即下面的u来通过该属性获得Employ表里符合条件的记录集

           protected void Button1_Click(object sender, EventArgs e)

        {

            linqtosqlDataContext dc = new linqtosqlDataContext();

                                        主要作用表          这里字段最好为唯一标识                          有外键关系的表

  var result = from u in dc.Company where u.CompanyName == "潍柴动力" select u.Employ;

 查询潍柴动力在招聘信息表里的招聘信息记录集,本来u.Employ返回的就是EntitySet<Employ>类型的集合,但返回到result就成了IQueryable<EntitySet<Employ>>了,所以要两层"剥皮"。

            foreach (var items in result)

            {

                foreach (var item in items)

                {

                    Label1.Text += item.Require;

                }

            }

        }

          protected void Button1_Click(object sender, EventArgs e)

        {

            linqtosqlDataContext dc = new linqtosqlDataContext();

  var result = from u in dc.Company where u.CompanyName == "潍柴动力" select u.Employ;

            foreach (var items in result)

            {

              items是EntitySet<Employ>类型

                for (int i = 0; i < items.Count; i++)

              {

              items[i]是Enpoly类型

                Label1.Text += items[i].Require + "<br/>";

     }

            }

        }

上面讲了EntitySet<T>的Count属性和其索引,对于其add()方法(向EntitySet<T>类型的集合(符合条件的记录集)里添加一个实体(一条记录)):
首先我们用我们惯用的方法:
   protected void Button2_Click1(object sender, EventArgs e)
        {
            linqtosqlDataContext dc = new linqtosqlDataContext();
            LeaveWord lw = new LeaveWord();
            lw.CompanyID = 19;
            lw.Content = "我的留言";
            lw.Reply = "公司回复";
       增加一条Member表中Accounter是xb的成员的留言
 var result = from u in dc.Member where u.Accounter.Equals("xb") select u.LeaveWord;
            foreach (var items in result)
            {
                items.Add(lw);
                Label2.Text =items.Count.ToString();
            }
            dc.SubmitChanges();
        }
虽然Label2显示增加了一条记录,但这似乎只是影响了内存中的相应集合,而代码dc.SubmitChanges();并没有将这些改变提交的数据库。:http://developer.51cto.com/art/200909/150713.htm
而另一种方法则能将这些改变提交到数据库:
   protected void Button2_Click1(object sender, EventArgs e)
        {
            linqtosqlDataContext dc = new linqtosqlDataContext();
            LeaveWord lw = new LeaveWord();
            lw.CompanyID = 19;
            lw.Content = "我的留言";
            lw.Reply = "公司回复";
 因为数据源是Table<Member>类型所以查询操作里的u是Member类型,而只有查询操作里的元素操作
http://kendezhu.iteye.com/blog/776538才能在这时返回EntitySet<T>类型的集合(即此时后面可加属性LeaveWord来返回LeaveWord表里符合条件的记录的集合)
EntitySet<LeaveWord> leaveword = dc.Member.Single(u => u.Accounter.Equals("xb")).LeaveWord;
            leaveword.Add(lw);
            Label2.Text =items.Count.ToString();
            dc.SubmitChanges();
        }

 

AddRange()方法(向EntitySet<T>类型的集合(符合条件的记录集)里添加实体集合(多条记录))

  protected void Button3_Click(object sender, EventArgs e)

        {

            linqtosqlDataContext db = new linqtosqlDataContext();

            List<LeaveWord> leaveword = new List<LeaveWord>();

            LeaveWord leaveword1 = new LeaveWord();

            leaveword1.CompanyID = 19;

            leaveword1.Content = "我的留言...";

            leaveword1.Reply = "公司回复...";

            LeaveWord leaveword2 = new LeaveWord();

            leaveword2.CompanyID = 19;

            leaveword2.Content = "我的留言......";

            leaveword2.Reply = "公司回复......";

            leaveword.Add(leaveword1);

            leaveword.Add(leaveword2);

            EntitySet<LeaveWord> leavewords = db.Member.Single(u => u.Accounter.Equals("xb")).LeaveWord;

 由提示得知AddRange方法的参数类型是IEnumerable<T>,所以我们用到了AsEnumerable()(看http://kendezhu.iteye.com/blog/776538的7.数据类型转换操作)

            leavewords.AddRange(leaveword.AsEnumerable());

            db.SubmitChanges();

            Label3.Text = leavewords.Count.ToString();

        }

 

Insert()方法(向EntitySet<T>类型的集合(符合条件的记录集)里的指定位置添加一个实体(一条记录))

该方法与Add()差不多,只不过多了一个int类型的参数: 如leaveword.Insert(1,lw);

 

Remove()方法(返回值是布尔类型,从EntitySet<T>类型的集合(符合条件的记录集)里删除一个实体(一条记录))

Removeat()方法(从EntitySet<T>类型的集合(符合条件的记录集)里删除一个指定位置的实体(一条记录))

Clear()方法(从EntitySet<T>类型的集合(符合条件的记录集)里删除所有实体(所有记录)

Assign()方法(将一个EntitySet<T>类型的集合里的实体分配到另一个EntitySet<T>类型的集合里,不保留原EntitySet<T>类型的集合里的实体)

以后上4个方法无法在.NET 3.5中用(.NET 3.5的bug,使用时会出现指定转换无效的错误,当然前提是你的数据库里的表设置了主键。。好像就可以了吧),在.NET 4.0(无法在VS2008中使用,因为在新建项目时无法在右上角选择.NET Framework 4.0,并且你可以在引用中选择一个程序集看看它的属性里的版本)中使用则一切OK(.NET 4.0在安装VS2010时会随机安装,其独立安装版本的官方下载地址:http://www.microsoft.com/downloads/details.aspx?FamilyID=0a391abd-25c1-4fc0-919f-b21f31ab88b7&displayLang=zh-cn,在其概述里的数据访问和建模的改进中我们可以看到其对LINQ的改进)

 

 protected void Button4_Click(object sender, EventArgs e)

        {

            linqtosqlDataContext db = new linqtosqlDataContext();

            EntitySet<LeaveWord> leaveword = db.Member.First(u => u.Accounter.Equals("xb")).LeaveWord;

            leaveword.Remove(leaveword[4]);

            leaveword.RemoveAt(5);

            leaveword.Clear();

            db.SubmitChanges();       指定的转换无效   这是.net 3.5的bug .net 4.0就可以了

            Label4.Text =leaveword.Count.ToString();

        }

 

 protected void Button7_Click(object sender, EventArgs e)

        {

    Assign()方法(将一个EntitySet<T>类型的集合里的实体分配到另一个EntitySet<T>类型的集合里)

            linqtosqlDataContext db = new linqtosqlDataContext();

            EntitySet<LeaveWord> leaveword1 = db.Member.Single(u => u.Accounter.Equals("xb")).LeaveWord;

            EntitySet<LeaveWord> leaveword2 = db.Member.Single(u => u.Accounter.Equals("GJ")).LeaveWord;

            leaveword2.Assign(leaveword1);

            db.SubmitChanges();   指定的转换无效     这是.net 3.5的bug .net 4.0就可以了

        }

 

处理EntityRef<T>类型的结果   using System.Data.Linq;

EntityRef<T>与EntitySet<T>刚好相反,反映到数据库里就是起次要作用的表里的那些符合条件的集合在起主要作用的表里所对应的那一条记录(我们可以在那些符合条件的集合里任意找一条记录(任意一个实体)来找它们在主要作用表里共同对应的那条记录)到*.designer.cs里看,会发现EntityRef<T>与EntitySet<T>的属性是对应出现的(即主要作用表的实体类有EntitySet<T>类型的属性,与其有关联关系的次要作用表的实体类就有EntityRef<T>类型的属性)所以EntityRef<T>与EntitySet<T>是不同的:通过上面的多次使用,我们知道通过EntitySet<T>类型的属性返回的是一个实体集(符合条件的记录集),而通过类型EntityRef<T>类型的属性返回的只是一个实体(一条记录),并且该实体的类型是T,这主要是通过EntityRef<T>的Entity属性(返回EntityRef<T>里的T类型的一条实体)实现的,这在*.designer.cs里也会看出来。

 protected void Button9_Click(object sender, EventArgs e)

        {

            linqtosqlDataContext db = new linqtosqlDataContext();

返回一条记录所以不写EntityRef<Member>  通过任意一条记录来找  

    Member member = db.LeaveWord.First(u => u.Accounter.Equals("霸气十足")).Member;

                                                                                                                      Member类型的属性(属性内部对EntityRef<T>类型的字段使用了Entity属性)

   在留言表里找到一条霸气十足的留言,通过它找到位与会员表里有关霸气十足的那条记录,找其姓名

            Label9.Text += member.Name;

        }

 

 protected void Button10_Click(object sender, EventArgs e)

        {

            linqtosqlDataContext db = new linqtosqlDataContext();

            查询所有的留言内容及它们的留言者姓名

            foreach (var leaveword in db.LeaveWord)

            {

                Label10.Text += leaveword.Member.Name + leaveword.Content + "<br/>";

            }

        }

 

现在我们来学习一下LINQ中与存储过程和函数有关的三种接口IExecuteResult,ISingleResult<T>,IMultipleResult

首先我们前面讲过在LINQ中使用存储过程和函数很简单,只要将其拖入dbml右边的窗口就行,保存后到*.designer.cs里可以在上下文类里可以看到已经为其生成了相应的方法了,通过观察我们看到这些方法中对于增删改的存储过程其返回值类型都是int类型,对于查询的存储过程其返回值类型有的是ISingleResult<存储过程名Result>类型(当查询返回单个序列时,感觉像datatable),有的是IMultipleResult类型(当查询返回多个序列时(如存储过程有多个Select语句)感觉像dataset,IMultipleResults接口还定义了GetResult<T>()方法来确定到底取哪个序列)。而无论是增删改还是查询都是将获取的数据保存为IExecuteResult类型的result对象,然后获取result对象的ReturnValue属性(IExecuteResult接口的属性,该属性返回存储过程返回的值,类型为Object)的值,将其强制转换为int,ISingleResult<存储过程名Result>或IMultipleResult类型。这些操作我们在保存时自动生成的,所以其实我们在运用时不必关这些,最多在不确定该方法返回什么类型时进来确定一下:

int的情况(适用于起增删改作用的存储过程)

  protected void Button12_Click(object sender, EventArgs e)

        {

            添加一家公司

            string cname = TextBox2.Text;

            string cinfo = TextBox3.Text;

            string cprovince = TextBox4.Text;

            string ccity = TextBox5.Text;

            int comid = int.Parse(TextBox6.Text);  管理员ID

            linqtosqlDataContext db = new linqtosqlDataContext();

   int result = db.ProAddCom(cname, cinfo, cprovince, ccity, comid);  失败返回-1,成功返回非-1一般为0吧

            Label12.Text = result.ToString();

        }

ISingleResult<存储过程名Result>的情况(适用于大部分查询的存储过程)

 protected void Button11_Click(object sender, EventArgs e)

        {

            string city = TextBox1.Text;

            linqtosqlDataContext db = new linqtosqlDataContext();

            根据城市名查询公司

            ISingleResult<ProSelComByCityResult> result = db.ProSelComByCity(city);

            foreach (var item in result)

            {

        可以看出此时item是ProSelComByCityResult类型,此时该类型就代表一条记录的类型

                Label11.Text +=item.CompanyID+" "+ item.CompanyName;

            }

        }

IMultipleResult的情况(暂时只知道适用于有多个select语句的存储过程)

假设我们的存储过程ProSelCpAndCm中用两个Select分别查询了公司表和管理员表

select * from company

select * from commanager

 protected void Button13_Click(object sender, EventArgs e)

        {

            linqtosqlDataContext db = new linqtosqlDataContext();

            IMultipleResults result = db.ProSelCpAndCm();

       通过IMultipleResults的GetResult<T>()方法获取想得到的序列,通过T来区别,返回类型是IEnumerable<T>,很像dataset[int/string]来获取某个datatable

            IEnumerable<company> companys=from u in result.GetResult<company>() where u.company.ID>1 select u;

               foreach (var item in companys)

            {

                Label11.Text +=item.CompanyID+" "+ item.CompanyName;

            }

            GridView1.DataSource=companys;   GridView绑定了到了类型为IEnumerable<T>的数据源,其可以绑定的数据源类型继承自IListSource,IEnumerable或IDataSource(datatable继承自IListSource)

            GridView1.DataBind();

            IEnumerable<commanager> commanagers=result.GetResult<commanager>();

              foreach (var item in commanagers)

            {

                Label12.Text +=item.CommanagerID+" "+ item.CommanagerName;

            }

            GridView2.DataSource=commanagers;

            GridView2.DataBind();

        }

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics