LINQ中文教程

   LINQ中文教程

 

LINQ初体验之LINQ to Object.......................................................................................... 1

一步一步学Linq to sql(一):预备知识................................................................... 4

一步一步学Linq to sql(二):DataContext与实体.................................................... 9

一步一步学Linq to sql(三):增删改..................................................................... 15

一步一步学Linq to sql(四):查询句法................................................................. 21

一步一步学Linq to sql(五):存储过程................................................................. 35

一步一步学Linq to sql(六):探究特性................................................................. 46

一步一步学Linq to sql(七):并发与事务.............................................................. 53

一步一步学Linq to sql(八):继承与关系.............................................................. 60

一步一步学Linq to sql(九):其它补充................................................................. 69

一步一步学Linq to sql(十):分层构架的例子....................................................... 73

Linq To Xml学习............................................................................................................ 80

Linq To Xml学习 - 1. 概述.................................................................................. 80

Linq To Xml学习 - 2 编程概述............................................................................ 82

C# 2008 学习笔记 - LINQ to XML.................................................................................. 87

一、命名空间.......................................................................................................... 87

二、编程方式创建XML文档................................................................................... 88

三、使用 LINQ 查询创建XML文档....................................................................... 89

四、加载和解析XML内容...................................................................................... 90

六、遍历内存中的XML文档................................................................................... 90

七、修改 XML文档................................................................................................ 92

使用linq to xml 快速创建自己的Rss........................................................................... 93

 

 

 

 

 

 

 

 

 

LINQ初体验之LINQ to Object

  VS2008的发布,最激动人心的不过是LINQ的诞生。

  What‘s LINQ? Language Integrated Query 是也。说得再明白一些,这是编程语言的一种新特性,能够将数据查询语句集成到编程语言中。目前,LINQ支持的语言有C# 和 VB。(源码网整理:www.codepub.com)

  为啥会有LINQ,主要还是因为现在的数据格式越来越多,数据库、XML、数组、哈希表……每一种都有自己操作数据的方式,学起来费事费力。于是,就有了LINQ诞生的理由——以一种统一的方式操作各种数据源,减少数据访问的复杂性。

  LINQ带来很多开发上的便利。首先,他可以利用Visual Studio这个强大的IDE(这话决不是吹,Visual Studio绝对是最好用的开发工具之一),至少用Visual Studio来写SQL语句,可以有智能感知了,比起从前用查询分析器写存储过程的感觉好多了!其次,它可以把数据当成一个对象来操作,即 Data == Object? 的问题。

  LINQ目前可以对XML, Object, SQL做数据访问,今后还会有LINQ to Entity的功能。

  说来惭愧,我也是刚刚才接触LINQ,先从最简单的开始吧,做一个LINQ to Object的例子,实现一个对数组的操作。这个例子套用了今年TechED中海洋兄讲的例子,在此声明。

  在这个例子中,我会先通过GetMethods的方法,拿到string的所有方法,接下来,就看LINQ的厉害了,这里是选出所有非静态的方法签名。

  


MethodInfo[] methods = typeof(string).GetMethods();
  var result = from m in methods
  where m.IsStatic != true
  select m.Name;
  foreach (var r in result)
  {
  Console.WriteLine(r.ToString());
  }
  Console.ReadLine();

  例子虽然简单,确能从中看出LINQ的一些端倪。首先,var是什么东东?

  看起来,有点像javascript里面的弱类型的变量声明。但是,C#是强类型的,尽管你用var来声明,编译器还是可以根据上下文推倒出它当前的类型。比如这个例子里面,result就是IEnumerable 类型的。在这里面,写IEnumerable和写var是一样效果的,显然,var会简单得多。你不用考虑数据操作的返回值是什么类型,还能享受强类型声明带来的方便实惠……

  还有from m in methods这句,m是什么东西,m是隐式声明的一个变量,尽管没有声明,但编译器根据上下文,推断出它的类型是MethodInfo型的!.NET Framework 3.5的编译器的确是聪明了很多 ^^

  上面这个例子运行起来的结果中有很多重复的记录,我们可以用distinct()来过滤掉重复的,和SQL还是很相似的说。

  


var result = (from m in methods
  where m.IsStatic != true
  select m.Name).Distinct();

  或者用group by 也可以

  


var result = from m in methods
  where m.IsStatic != true
  group m by m.Name;

  但是这样子写,在输出的时候,显示的是 System.Linq.Lookup`2+Grouping[System.String,System.Reflection.MethodInfo],所以,再做一些修改

  


var result = from m in methods
  where m.IsStatic != true
  group m by m.Name into g
  select new { MethodName = g.Key, Overload = g.Count() };

  这里面又有一些新鲜的了——select new { MethodName = g.Key, Overload = g.Count() },先来看一个简单一些的例子:

  


class MyClass
  {
  public string MethodName { get; set; }
  public int Overload { get; set; }
  }
  class Program
  {
  MyClass mc = new MyClass { MethodName = "aaa", Overload = 2 };
  }

  大括号里面的叫类初始化器,省去了构造函数,在new的同时,给对象的属性赋值。

  这时候再回来看看select new { MethodName = g.Key, Overload = g.Count() },跟这个类初始化器很像吧,但是它更偷懒,new一个新对象,居然连类名都不写。没错,这就叫匿名类。不用写类的声明,直接实例化。类的名字是由编译器在编译的时候自动生成的,也就是说,你在new的时候根本不知道这个类叫啥名,因此,这里就只能用var了。这就更看出var的厉害了,不仅仅是写起来方便这么简单,在用到匿名类的时候,没有类名,这时候只能用var,嘿嘿!

一步一步学Linq to sql(一):预备知识

2007-08-14 09:00 作者: lovecherry 出处: 天极网 责任编辑:>dizzarz

  什么是Linq to sql

  Linq to sql(或者叫DLINQ)是LINQ(.NET语言集成查询)的一部分,全称基于关系数据的 .NET 语言集成查询,用于以对象形式管理关系数据,并提供了丰富的查询功能,它和Linq to xml、Linq to objects、Linq to dataset、Linq to entities等组成了强大的LINQ。

  要学好LINQ查询语法,就不得不先理解C# 3.0的一些新特性,下面一一简单介绍。

  隐含类型局部变量

  


var age = 26;
  var username = "zhuye";
  var userlist = new [] {"a","b","c"};
  foreach(var user in userlist)
  Console.WriteLine(user);

  纯粹给懒人用的var关键字,告诉编译器(对于CLR来说,它是不会知道你是否使用了var,苦力是编译器出的),你自己推断它的类型吧,我不管了。但是既然让编译器推断类型就必须声明的时候赋值,而且不能是null值。注意,这只能用于局部变量,用于字段是不可以的。

  匿名类型

  


var data = new {username = "zhuye",age = 26};
  Console.WriteLine("username:{0} age:{1}", data.username, data.age);

  匿名类型允许开发人员定义行内类型,无须显式定义类型。常和var配合使用,var用于声明匿名类型。定义一个临时的匿名类型在LINQ查询句法中非常常见,我们可以很方便的实现对象的转换和投影。

  扩展方法

 


 public static class helper
  {
  public static string MD5Hash(this string s)
  {
  return System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(s,"MD5");
  }
  public static bool In(this object o, IEnumerable b)
  {
  foreach(object obj in b)
  {
  if(obj==o)
  return true;
  }
  return false;
  }
  }
  // 调用扩展方法
  Console.WriteLine("123456".MD5Hash());
  Console.WriteLine("1".In(new[]{"1","2","3"}));

  很多时候我们需要对CLR类型进行一些操作,苦于无法扩展CLR类型的方法,只能创建一些helper方法,或者生成子类。扩展方法使得这些需求得以实现,同时也是实现LINQ的基础。定义扩展方法需要注意,只能在静态类中定义并且是静态方法,如果扩展方法名和原有方法名发生冲突,那么扩展方法将失效。

  自动属性

  


public class Person
  {
  public string username { get; protected set; }
  public int age { get; set; }
  public Person()
  {
  this.username = "zhuye";
  }
  }
  Person p = new Person();
  //p.username = "aa";
  Console.WriteLine(p.username);

  意义不是很大,纯粹解决机械劳动。编译器自动为你生成get、set操作以及字段,并且你不能使用字段也不能自定义get、set操作,不过你可以分别定义get和set的访问级别。

  对象初始化器

 


 public class Person
  {
  public string username { get; set; }
  public int age { get; set; }
  public override string ToString()
  {
  return string.Format("username:{0} age:{1}", this.username, this.age);
  }
  }
  Person p = new Person() {username = "zhuye", age=26};
  Console.WriteLine(p.ToString());

  编译器会自动为你做setter操作,使得原本几行的属性赋值操作能在一行中完成。这里需要注意:

  •   允许只给一部分属性赋值,包括internal访问级别
  •   可以结合构造函数一起使用,并且构造函数初始化先于对象初始化器执行

  集合初始化器

  


public class Person
  {
  public string username { get; set; }
  public int age { get; set; }
  public override string ToString()
  {
  return string.Format("username:{0} age:{1}", this.username, this.age);
  }
  }
  var persons = new List {
  new Person {username = "a", age=1},
  new Person {username = "b", age=2}};
  foreach(var p in persons)
  Console.WriteLine(p.ToString());

  编译器会自动为你做集合插入操作。如果你为Hashtable初始化的话就相当于使用了两个对象初始化器。

  Lambda表达式

 


 var list = new [] { "aa", "bb", "ac" };
  var result = Array.FindAll(list, s => (s.IndexOf("a") > -1));
  foreach (var v in result)
  Console.WriteLine(v);

  其实和2.0中的匿名方法差不多,都是用于产生内联方法,只不过Lambda表达式的语法更为简洁。语法如下:

  (参数列表) => 表达式或者语句块

  其中:

  参数个数:可以有多个参数,一个参数,或者无参数。

  表达式或者语句块:这部分就是我们平常写函数的实现部分(函数体)。

  前面的示例分别是1个参数的例子,下面结合扩展方法来一个复杂的例子:

  


public delegate int mydg(int a, int b);
  public static class LambdaTest
  {
  public static int oper(this int a, int b, mydg dg)
  {
  return dg(a, b);
  }
  }
  Console.WriteLine(1.oper(2, (a, b) => a + b));
  Console.WriteLine(2.oper(1, (a, b) => a - b));

  查询句法

 


 var persons = new List {
  new Person {username = "a", age=19},
  new Person {username = "b", age=20},
  new Person {username = "a", age=21},
  };
  var selectperson = from p in persons where p.age >= 20 select p.username.ToUpper();
  foreach(var p in selectperson)
  Console.WriteLine(p);

  查询句法是使用标准的LINQ查询运算符来表达查询时一个方便的声明式简化写法。该句法能在代码里表达查询时增进可读性和简洁性,读起来容易,也容易让人写对。Visual Studio 对查询句法提供了完整的智能感应和编译时检查支持。编译器在底层把查询句法的表达式翻译成明确的方法调用代码,代码通过新的扩展方法和Lambda表达式语言特性来实现。上面的查询句法等价于下面的代码:

  


var selectperson = persons.Where(p=>p.age>=20).Select(p=>p.username.ToUpper());

  LINQ查询句法可以实现90%以上T-SQL的功能(由于T-SQL是基于二维表的,所以LINQ的查询语法会比T-SQL更简单和灵活),但是由于智能感应的原因,select不能放在一开始就输入。

一步一步学Linq to sql(二):DataContext与实体

2007-08-24 00:00 作者: LoveCherry 出处: 天极网 责任编辑:>dizzarz

DataContext

       DataContext类型(数据上下文)是System.Data.Linq命名空间下的重要类型,用于把查询句法翻译成SQL语句,以及把数据从数据库返回给调用方和把实体的修改写入数据库。

  •        DataContext提供了以下一些使用的功能:
  •        以日志形式记录DataContext生成的SQL
  •        执行SQL(包括查询和更新语句)
  •        创建和删除数据库

     DataContext是实体和数据库之间的桥梁,那么首先我们需要定义映射到数据表的实体。

定义实体类



using System.Data.Linq.Mapping;

[Table(Name = "Customers")]
public class Customer
{
    [Column(IsPrimaryKey = true)]
    public string CustomerID {get; set;}

    [Column(Name = "ContactName")]
    public string Name { get; set; }

    [Column]
    public string City {get; set;}
}

  

     以Northwind数据库为例,上述Customers类被映射成一个表,对应数据库中的 Customers表。然后在类型中定义了三个属性,对应表中的三个字段。其中,CustomerID字段是主键,如果没有指定Column特性的Name属性,那么系统会把属性名作为数据表的字段名,也就是说实体类的属性名就需要和数据表中的字段名一致。

       现在,创建一个ASP.NET页面,然后在页面上加入一个GridView控件,使用下面的代码进行绑定数据:



using System.Data.Linq;

DataContext ctx = new DataContext("server=xxx;database=Northwind;uid=xxx;pwd=xxx");
Table Customers = ctx.GetTable();
GridView1.DataSource = from c in Customers where c.CustomerID.StartsWith("A") select new {顾客ID=c.CustomerID, 顾客名=c.Name, 城市=c.City};
GridView1.DataBind();

 

      使用DataContext类型把实体类和数据库中的数据进行关联。你可以直接在DataContext的构造方法中定义连接字符串,也可以使用IDbConnection:



using System.Data.SqlClient;

IDbConnection conn = new SqlConnection("server=xxx;database=Northwind;uid=xxx;pwd=xxx");
DataContext ctx = new DataContext(conn);

       之后,通过GetTable获取表示底层数据表的Table类型,显然,数据库中的Customers表的实体是Customer类型。随后的查询句法,即使你不懂SQL应该也能看明白。从Customers表中找出CustomerID以“A”开头的记录,并把CustomersID、Name以及City封装成新的匿名类型进行返回。

       结果如下图:

强类型DataContext



public partial class NorthwindDataContext : DataContext
{
    public Table Customers;
    public NorthwindDataContext(IDbConnection connection) : base(connection) { }
    public NorthwindDataContext(string connection) : base(connection) { }
}

       强类型数据上下文使代码更简洁:



NorthwindDataContext ctx = new NorthwindDataContext("server=xxx;database=Northwind;uid=xxx;pwd=xxx");
GridView1.DataSource = from c in ctx.Customers where c.CustomerID.StartsWith("A") select new { 顾客ID = c.CustomerID, 顾客名 = c.Name, 城市 = c.City };
GridView1.DataBind();

       DataContext其实封装了很多实用的功能,下面一一介绍。

日志功能



using System.IO;

NorthwindDataContext ctx = new NorthwindDataContext("server=xxx;database=Northwind;uid=xxx;pwd=xxx");
StreamWriter sw = new StreamWriter(Server.MapPath("log.txt"), true); // Append
ctx.Log = sw;
GridView1.DataSource = from c in ctx.Customers where c.CustomerID.StartsWith("A") select new { 顾客ID = c.CustomerID, 顾客名 = c.Name, 城市 = c.City };
GridView1.DataBind();
sw.Close();

       运行程序后在网站所在目录生成了log.txt,每次查询都会把诸如下面的日志追加到文本文件中:



SELECT [t0].[CustomerID], [t0].[ContactName], [t0].[City]
FROM [Customers] AS [t0]
WHERE [t0].[CustomerID] LIKE @p0
-- @p0: Input String (Size = 2; Prec = 0; Scale = 0) [A%]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1

       应该说这样的日志对于调试程序是非常有帮助的。

探究查询



using System.Data.Common;
using System.Collections.Generic;

NorthwindDataContext ctx = new NorthwindDataContext("server=xxx;database=Northwind;uid=xxx;pwd=xxx");
var select = from c in ctx.Customers where c.CustomerID.StartsWith("A") select new { 顾客ID = c.CustomerID, 顾客名 = c.Name, 城市 = c.City };
DbCommand cmd = ctx.GetCommand(select);
Response.Write(cmd.CommandText + "
");
foreach (DbParameter parm in cmd.Parameters)
    Response.Write(string.Format("参数名:{0},参数值:{1}
", parm.ParameterName, parm.Value));
Customer customer = ctx.Customers.First();
customer.Name = "zhuye";
IList queryText = ctx.GetChangeSet().ModifiedEntities;
Response.Write(((Customer)queryText[0]).Name);

       在这里,我们通过DataContext的GetCommand方法获取了查询对应的DbCommand,并且输出了CommandText和所有的DbParameter。之后,我们又通过GetChangeSet方法获取了修改后的实体,并输出了修改内容。

执行查询



NorthwindDataContext ctx = new NorthwindDataContext("server=xxx;database=Northwind;uid=xxx;pwd=xxx");
string newcity = "Shanghai";
ctx.ExecuteCommand("update Customers set City={0} where CustomerID like 'A%'", newcity);
IEnumerable customers = ctx.ExecuteQuery("select * from Customers where CustomerID like 'A%'");
GridView1.DataSource = customers;
GridView1.DataBind();

       前一篇文章已经说了,虽然Linq to sql能实现90%以上的TSQL功能。但是不可否认,对于复杂的查询,使用TSQL能获得更好的效率。因此,DataContext类型也提供了执行SQL语句的能力。代码的执行结果如下图:

创建数据库



testContext ctx = new testContext("server=xxx;database=testdb;uid=xxx;pwd=xxx");
ctx.CreateDatabase();

[Table(Name = "test")]
public class test
{
    [Column(IsPrimaryKey = true, IsDbGenerated = true)]
    public int ID { get; set; }

    [Column(DbType="varchar(20)")]
    public string Name { get; set; }
}

public partial class testContext : DataContext
{
    public Table test;
    public testContext(string connection) : base(connection) { }
}

testContext ctx = new testContext("server=xxx;database=testdb;uid=xxx;pwd=xxx");

ctx.CreateDatabase();

[Table(Name = "test")]

public class test

{

    [Column(IsPrimaryKey = true, IsDbGenerated = true)]

    public int ID { get; set; }

    [Column(DbType="varchar(20)")]

    public string Name { get; set; }

}

public partial class testContext : DataContext

{

    public Table test;

    public testContext(string connection) : base(connection) { }

}

       这段代码在数据库中创建了名为testdb的数据库,等同于下面的脚本:



CREATE TABLE [dbo].[test](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Name] [varchar](20) COLLATE Chinese_PRC_CI_AS NULL,
 CONSTRAINT [PK_test] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

       同时,DataContext还提供了DeleteDatabase()方法,在这里就不列举了。

使用DbDataReader数据源



using System.Data.SqlClient;

var conn = new SqlConnection("server=xxx;database=Northwind;uid=xxx;pwd=xxx");
var ctx = new DataContext(conn);
var cmd = new SqlCommand("select * from customers where CustomerID like 'A%'", conn);
conn.Open(); 
var reader = cmd.ExecuteReader();        
GridView1.DataSource = ctx.Translate(reader);
GridView1.DataBind();
conn.Close();

       你同样可以选择使用DataReader获取数据,增加了灵活性的同时也增加了性能。

看到这里,你可能会觉得手工定义和数据库中表对应的实体类很麻烦,不用担心,VS2008提供了自动生成实体类以及关系的工具,工具的使用将在以后讲解。今天就讲到这里,和DataContext相关的事务、加载选项、并发选项以及关系实体等高级内容也将在以后讲解。

一步一步学Linq to sql(三):增删改

2007-10-20 00:00 作者: lovecherry 出处: 天极网 责任编辑:>dizzarz

      示例数据库

字段名

字段类型

允许空

字段说明

ID

uniqueidentifier

表主键字段

UserName

varchar(50)

留言用户名

PostTime

datetime

留言时间

Message

varchar(400)

留言内容

IsReplied

bit

留言是否回复

Reply

varchar(400)

留言管理员回复

       在数据库中创建一个名为GuestBook的数据库,在里面创建一个tbGuestBook的表,结构如上表。

生成实体类

       右键点击网站项目,选择添加新项,然后选择“Linq to sql Classes”,命名为GuestBook。然后打开App_Code里面的GuestBook.dbml。设计视图上的文字提示你可以从服务器资源管理器或者攻击箱拖动项到设计界面上来创建实体类。

       那么,我们就在服务器资源管理器中创建一个指向GuestBook数据库的数据连接,然后把tbGuestBook表拖动到GuestBook.dbml的设计视图上,按CTRL+S保存。打开GuestBook.designer.cs可以发现系统自动创建了GuestBook数据库中tbGuestBook表的映射,如下图:

简易留言簿

       现在,我们就可以使用Linq to sql完成简易留言簿了。实现以下功能:

  •          发表留言(增)
  •          查看留言(查)
  •          管理员回复留言(改)
  •          管理员删除留言(删除)

首先,创建一个Default.aspx,在页面上加入一些控件:


            
            <%# Eval("Message")%>
            
            
            <%# Eval("PostTime")%> - <%# Eval("UserName")%>
            
            

            
            
            
            管理员回复:<%# Eval("IsReplied").ToString() == "False" ? "暂无" : Eval("Reply")%>
            
            
           
        




        姓名
        
        
        留言
        
        
        
        
        
        
        
            

        

        
        
    


       你可能很难想象,使用Linq to sql进行数据访问会是这么简单,后台代码:
public partial class _Default : System.Web.UI.Page 
{
    GuestBookDataContext ctx = new GuestBookDataContext("server=xxx;database=GuestBook;uid=xxx;pwd=xxx");
       
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            SetBind();
        }
    }
    protected void btn_SendMessage_Click(object sender, EventArgs e)
    {
        tbGuestBook gb = new tbGuestBook();
gb.ID = Guid.NewGuid();
        gb.UserName = tb_UserName.Text;
        gb.Message = tb_Message.Text;
        gb.IsReplied = false;
        gb.PostTime = DateTime.Now;
        ctx.tbGuestBooks.Add(gb);
        ctx.SubmitChanges();
        SetBind();
    }
    private void SetBind()
    {
        rpt_Message.DataSource = from gb in ctx.tbGuestBooks orderby gb.PostTime descending select gb;
        rpt_Message.DataBind();
    }
}

       前面创建Linq to sql Classes的时候我们输入名字GuestBook,系统就为我们自动创建了GuestBookDataContext(你也可以在GuestBook.Designer.cs中找到类定义)。在绑定的时候我们使用查询句法查询留言表中所有留言,按照发表时间倒序(天哪?这是数据访问吗?好像仅仅定义了一句SQL啊)。在发表留言按钮中,我们为一个tbGuestBook赋值,然后把它加入留言表,再提交更改,就这样完成了记录的插入。

       运行效果如下图:

       然后,再创建一个Admin.aspx,前台代码如下:


            
            <%# Eval("Message")%>
            
            
            <%# Eval("PostTime")%> - <%# Eval("UserName")%>
            
            

            
            
            
            '/>
            管理员回复:'/>
            '/>
            
            
           
        




        
        
        
            

        

        
        
    

       后台代码:




public partial class Admin : System.Web.UI.Page
{
    GuestBookDataContext ctx = new GuestBookDataContext("server=xxx;database=GuestBook;uid=xxx;pwd=xxx");
        
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            SetBind();
        }
    }

    private void SetBind()
    {
        rpt_Message.DataSource = from gb in ctx.tbGuestBooks orderby gb.PostTime descending select gb;
        rpt_Message.DataBind();
    }
    protected void rpt_Message_ItemCommand(object source, RepeaterCommandEventArgs e)
    {
        if (e.CommandName == "DeleteMessage")
        {
            StreamWriter sw = new StreamWriter(Server.MapPath("log.txt"), true); 
            ctx.Log = sw;
            tbGuestBook gb = ctx.tbGuestBooks.Single(b => b.ID == new Guid(e.CommandArgument.ToString()));
            ctx.tbGuestBooks.Remove(gb);
            ctx.SubmitChanges();
            SetBind();
            sw.Close();
        }
        if (e.CommandName == "SendReply")
        {
            StreamWriter sw = new StreamWriter(Server.MapPath("log.txt"), true); 
            ctx.Log = sw;
            tbGuestBook gb = ctx.tbGuestBooks.Single(b => b.ID == new Guid(e.CommandArgument.ToString()));
            gb.Reply = ((TextBox)e.Item.FindControl("tb_Reply")).Text;
            gb.IsReplied = true;
            ctx.SubmitChanges();
            SetBind();
            sw.Close();
        }
    }
}

       运行效果如下图:

      在这里,我们通过Single方法获取一条记录,也就是一个tbGuestBook实例,更新了一些属性后保存也就完成了改这个操作。删除操作更简单,只需要从表中移除对象。你是不是觉得好像不是在操作数据库,像在操作内存中的对象。

       由于写了日志,看看改和删操作会是怎么样的SQL?


UPDATE [dbo].[tbGuestBook]
SET [IsReplied] = @p4, [Reply] = @p5
WHERE ([ID] = @p0) AND ([UserName] = @p1) AND ([PostTime] = @p2) AND ([Message] = @p3) AND (NOT ([IsReplied] = 1)) AND ([Reply] IS NULL)
-- @p0: Input Guid (Size = 0; Prec = 0; Scale = 0) [00000000-0000-0000-0000-000000000000]
-- @p1: Input String (Size = 4; Prec = 0; Scale = 0) [ghgh]
-- @p2: Input DateTime (Size = 0; Prec = 0; Scale = 0) [2007-8-16 10:20:09]
-- @p3: Input String (Size = 3; Prec = 0; Scale = 0) [ghj]
-- @p4: Input Boolean (Size = 0; Prec = 0; Scale = 0) [True]
-- @p5: Input String (Size = 3; Prec = 0; Scale = 0) [qqq]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1

DELETE FROM [dbo].[tbGuestBook] WHERE ([ID] = @p0) AND ([UserName] = @p1) AND ([PostTime] = @p2) AND ([Message] = @p3) AND (NOT ([IsReplied] = 1)) AND ([Reply] = @p4)
-- @p0: Input Guid (Size = 0; Prec = 0; Scale = 0) [158ec941-13ff-4093-bd8b-9fceae152171]
-- @p1: Input String (Size = 2; Prec = 0; Scale = 0) [44]
-- @p2: Input DateTime (Size = 0; Prec = 0; Scale = 0) [2007-8-16 9:56:19]
-- @p3: Input String (Size = 2; Prec = 0; Scale = 0) [44]
-- @p4: Input String (Size = 3; Prec = 0; Scale = 0) [222]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1

       今天就讲到这里,下次将系统介绍查询句法。

一步一步学Linq to sql(四):查询句法

2007-10-18 09:00 作者: lovecherry 出处: 天极网 责任编辑:>dizzarz

  select

  描述:查询顾客的公司名、地址信息

  查询句法:

  var 构建匿名类型1 = from c in ctx.Customers

  


select new
  {
  公司名 = c.CompanyName,
  地址 = c.Address
  };

  对应SQL:

 


 SELECT [t0].[CompanyName], [t0].[Address]
  FROM [dbo].[Customers] AS [t0]

  描述:查询职员的姓名和雇用年份

  查询句法:

  


var 构建匿名类型2 = from emp in ctx.Employees
  select new
  {
  姓名 = emp.LastName + emp.FirstName,
  雇用年 = emp.HireDate.Value.Year
  };

  对应SQL:

  


SELECT [t0].[LastName] + [t0].[FirstName] AS [value], DATEPART(Year, [t0].[HireDate]) AS [value2]
  FROM [dbo].[Employees] AS [t0]

  描述:查询顾客的ID以及联系信息(职位和联系人)

  查询句法:

  var 构建匿名类型3 = from c in ctx.Customers

  


select new
  {
  ID = c.CustomerID,
  联系信息 = new
  {
  职位 = c.ContactTitle,
  联系人 = c.ContactName
  }
  };

  对应SQL:

 


 SELECT [t0].[CustomerID], [t0].[ContactTitle], [t0].[ContactName]
  FROM [dbo].[Customers] AS [t0]

  描述:查询订单号和订单是否超重的信息

  查询句法:

 


 var select带条件 = from o in ctx.Orders
  select new
  {
  订单号 = o.OrderID,
  是否超重 = o.Freight > 100 ? "是" : "否"
  };

  对应SQL:

 


 SELECT [t0].[OrderID],
  (CASE
  WHEN [t0].[Freight] > @p0 THEN @p1
  ELSE @p2
  END) AS [value]
  FROM [dbo].[Orders] AS [t0]
  -- @p0: Input Currency (Size = 0; Prec = 19; Scale = 4) [100]
  -- @p1: Input String (Size = 1; Prec = 0; Scale = 0) [是]
  -- @p2: Input String (Size = 1; Prec = 0; Scale = 0) [否]
  

  where

      描述:查询顾客的国家、城市和订单数信息,要求国家是法国并且订单数大于5

  查询句法:

  


var 多条件 = from c in ctx.Customers
  where c.Country == "France" && c.Orders.Count > 5
  select new
  {
  国家 = c.Country,
  城市 = c.City,
  订单数 = c.Orders.Count
  };

  对应SQL:

 


 SELECT [t0].[Country], [t0].[City], (
  SELECT COUNT(*)
  FROM [dbo].[Orders] AS [t2]
  WHERE [t2].[CustomerID] = [t0].[CustomerID]
  ) AS [value]
  FROM [dbo].[Customers] AS [t0]
  WHERE ([t0].[Country] = @p0) AND (((
  SELECT COUNT(*)
  FROM [dbo].[Orders] AS [t1]
  WHERE [t1].[CustomerID] = [t0].[CustomerID]
  )) > @p1)
  -- @p0: Input String (Size = 6; Prec = 0; Scale = 0) [France]
  -- @p1: Input Int32 (Size = 0; Prec = 0; Scale = 0) [5]
 

  orderby

      描述:查询所有没有下属雇员的雇用年和名,按照雇用年倒序,按照名正序

  查询句法:


  var 排序 = from emp in ctx.Employees
  where emp.Employees.Count == 0
  orderby emp.HireDate.Value.Year descending, emp.FirstName ascending
  select new
  {
  雇用年 = emp.HireDate.Value.Year,
  名 = emp.FirstName
  };

  对应SQL:

 


 SELECT DATEPART(Year, [t0].[HireDate]) AS [value], [t0].[FirstName]
  FROM [dbo].[Employees] AS [t0]
  WHERE ((
  SELECT COUNT(*)
  FROM [dbo].[Employees] AS [t1]
  WHERE [t1].[ReportsTo] = [t0].[EmployeeID]
  )) = @p0
  ORDER BY DATEPART(Year, [t0].[HireDate]) DESC, [t0].[FirstName]
  -- @p0: Input Int32 (Size = 0; Prec = 0; Scale = 0) [0]

  分页

  描述:按照每页10条记录,查询第二页的顾客

  查询句法:

 


 var 分页 = (from c in ctx.Customers select c).Skip(10).Take(10);

  对应SQL:

 


 SELECT TOP 10 [t1].[CustomerID], [t1].[CompanyName], [t1].[ContactName], [t1].[ContactTitle], [t1].[Address], [t1].[City], [t1].[Region], [t1].[PostalCode], [t1].[Country], [t1].[Phone], [t1].[Fax]
  FROM (
  SELECT ROW_NUMBER() OVER (ORDER BY [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax]) AS [ROW_NUMBER], [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax]
  FROM [dbo].[Customers] AS [t0]
  ) AS [t1]
  WHERE [t1].[ROW_NUMBER] > @p0
  -- @p0: Input Int32 (Size = 0; Prec = 0; Scale = 0) [10]

  分组

  描述:根据顾客的国家分组,查询顾客数大于5的国家名和顾客数

  查询句法:

  


var 一般分组 = from c in ctx.Customers
  group c by c.Country into g
  where g.Count() > 5
  orderby g.Count() descending
  select new
  {
  国家 = g.Key,
  顾客数 = g.Count()
  };

  对应SQL:

  


SELECT [t1].[Country], [t1].[value3] AS [顾客数]
  FROM (
  SELECT COUNT(*) AS [value], COUNT(*) AS [value2], COUNT(*) AS [value3], [t0].[Country]
  FROM [dbo].[Customers] AS [t0]
  GROUP BY [t0].[Country]
  ) AS [t1]
  WHERE [t1].[value] > @p0
  ORDER BY [t1].[value2] DESC
  -- @p0: Input Int32 (Size = 0; Prec = 0; Scale = 0) [5]

  描述:根据国家和城市分组,查询顾客覆盖的国家和城市

  查询句法:

 


 var 匿名类型分组 = from c in ctx.Customers
  group c by new { c.City, c.Country } into g
  orderby g.Key.Country, g.Key.City
  select new
  {
  国家 = g.Key.Country,
  城市 = g.Key.City
  };

  对应SQL:

  


SELECT [t1].[Country], [t1].[City]
  FROM (
  SELECT [t0].[City], [t0].[Country]
  FROM [dbo].[Customers] AS [t0]
  GROUP BY [t0].[City], [t0].[Country]
  ) AS [t1]
  ORDER BY [t1].[Country], [t1].[City]

  描述:按照是否超重条件分组,分别查询订单数量

  查询句法:

  


var 按照条件分组 = from o in ctx.Orders
  group o by new { 条件 = o.Freight > 100 } into g
  select new
  {
  数量 = g.Count(),
  是否超重 = g.Key.条件 ? "是" : "否"
  };

  对应SQL:

 


 SELECT
  (CASE
  WHEN [t2].[value2] = 1 THEN @p1
  ELSE @p2
  END) AS [value], [t2].[value] AS [数量]
  FROM (
  SELECT COUNT(*) AS [value], [t1].[value] AS [value2]
  FROM (
  SELECT
  (CASE
  WHEN [t0].[Freight] > @p0 THEN 1
  WHEN NOT ([t0].[Freight] > @p0) THEN 0
  ELSE NULL
  END) AS [value]
  FROM [dbo].[Orders] AS [t0]
  ) AS [t1]
  GROUP BY [t1].[value]
  ) AS [t2]
  -- @p0: Input Currency (Size = 0; Prec = 19; Scale = 4) [100]
  -- @p1: Input String (Size = 1; Prec = 0; Scale = 0) [是]
  -- @p2: Input String (Size = 1; Prec = 0; Scale = 0) [否]

  distinct

  描述:查询顾客覆盖的国家

  查询句法:

  


var 过滤相同项 = (from c in ctx.Customers orderby c.Country select c.Country).Distinct();

  对应SQL:

  


SELECT DISTINCT [t0].[Country]
  FROM [dbo].[Customers] AS [t0]

  union

  描述:查询城市是A打头和城市包含A的顾客并按照顾客名字排序

  查询句法:

  


var 连接并且过滤相同项 = (from c in ctx.Customers where c.City.Contains("A") select c).Union
  (from c in ctx.Customers where c.ContactName.StartsWith("A") select c).OrderBy(c => c.ContactName);

  对应SQL:


  SELECT [t3].[CustomerID], [t3].[CompanyName], [t3].[ContactName], [t3].[ContactTitle], [t3].[Address], [t3].[City], [t3].[Region], [t3].[PostalCode], [t3].[Country], [t3].[Phone], [t3].[Fax]
  FROM (
  SELECT [t2].[CustomerID], [t2].[CompanyName], [t2].[ContactName], [t2].[ContactTitle], [t2].[Address], [t2].[City], [t2].[Region], [t2].[PostalCode], [t2].[Country], [t2].[Phone], [t2].[Fax]
  FROM (
  SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax]
  FROM [dbo].[Customers] AS [t0]
  WHERE [t0].[City] LIKE @p0
  UNION
  SELECT [t1].[CustomerID], [t1].[CompanyName], [t1].[ContactName], [t1].[ContactTitle], [t1].[Address], [t1].[City], [t1].[Region], [t1].[PostalCode], [t1].[Country], [t1].[Phone], [t1].[Fax]
  FROM [dbo].[Customers] AS [t1]
  WHERE [t1].[ContactName] LIKE @p1
  ) AS [t2]
  ) AS [t3]
  ORDER BY [t3].[ContactName]
  -- @p0: Input String (Size = 3; Prec = 0; Scale = 0) [%A%]
  -- @p1: Input String (Size = 2; Prec = 0; Scale = 0) [A%]

  concat

  描述:查询城市是A打头和城市包含A的顾客并按照顾客名字排序,相同的顾客信息不会过滤

  查询句法:

 


 var 连接并且不过滤相同项 = (from c in ctx.Customers where c.City.Contains("A") select c).Concat
  (from c in ctx.Customers where c.ContactName.StartsWith("A") select c).OrderBy(c => c.ContactName);

  对应SQL:

SELECT [t3].[CustomerID], [t3].[CompanyName], [t3].[ContactName], [t3].[ContactTitle], [t3].[Address], [t3].[City], [t3].[Region], [t3].[PostalCode], [t3].[Country], [t3].[Phone], [t3].[Fax]

  FROM (

  SELECT [t2].[CustomerID], [t2].[CompanyName], [t2].[ContactName], [t2].[ContactTitle], [t2].[Address], [t2].[City], [t2].[Region], [t2].[PostalCode], [t2].[Country], [t2].[Phone], [t2].[Fax]

  FROM (

  SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax]

  FROM [dbo].[Customers] AS [t0]

  WHERE [t0].[City] LIKE @p0

  UNION ALL

  SELECT [t1].[CustomerID], [t1].[CompanyName], [t1].[ContactName], [t1].[ContactTitle], [t1].[Address], [t1].[City], [t1].[Region], [t1].[PostalCode], [t1].[Country], [t1].[Phone], [t1].[Fax]

  FROM [dbo].[Customers] AS [t1]

  WHERE [t1].[ContactName] LIKE @p1

  ) AS [t2]

  ) AS [t3]

  ORDER BY [t3].[ContactName]

  -- @p0: Input String (Size = 3; Prec = 0; Scale = 0) [%A%]

  -- @p1: Input String (Size = 2; Prec = 0; Scale = 0) [A%]

  取相交项

  描述:查询城市是A打头的顾客和城市包含A的顾客的交集,并按照顾客名字排序

  查询句法:

  


var 取相交项 = (from c in ctx.Customers where c.City.Contains("A") select c).Intersect
  (from c in ctx.Customers where c.ContactName.StartsWith("A") select c).OrderBy(c => c.ContactName);

  对应SQL:


  SELECT [t1].[CustomerID], [t1].[CompanyName], [t1].[ContactName], [t1].[ContactTitle], [t1].[Address], [t1].[City], [t1].[Region], [t1].[PostalCode], [t1].[Country], [t1].[Phone], [t1].[Fax]
  FROM (
  SELECT DISTINCT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax]
  FROM [dbo].[Customers] AS [t0]
  ) AS [t1]
  WHERE (EXISTS(
  SELECT NULL AS [EMPTY]
  FROM [dbo].[Customers] AS [t2]
  WHERE ([t1].[CustomerID] = [t2].[CustomerID]) AND ([t2].[ContactName] LIKE @p0)
  )) AND ([t1].[City] LIKE @p1)
  ORDER BY [t1].[ContactName]
  -- @p0: Input String (Size = 2; Prec = 0; Scale = 0) [A%]
  -- @p1: Input String (Size = 3; Prec = 0; Scale = 0) [%A%]

  排除相交项

  描述:查询城市包含A的顾客并从中删除城市以A开头的顾客,并按照顾客名字排序

  查询句法:

  


var 排除相交项 = (from c in ctx.Customers where c.City.Contains("A") select c).Except
  (from c in ctx.Customers where c.ContactName.StartsWith("A") select c).OrderBy(c => c.ContactName);

  对应SQL:

  SELECT [t1].[CustomerID], [t1].[CompanyName], [t1].[ContactName], [t1].[ContactTitle], [t1].[Address], [t1].[City], [t1].[Region], [t1].[PostalCode], [t1].[Country], [t1].[Phone], [t1].[Fax]

  FROM (

  SELECT DISTINCT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax]

  FROM [dbo].[Customers] AS [t0]

  ) AS [t1]

  WHERE (NOT (EXISTS(

  SELECT NULL AS [EMPTY]

  FROM [dbo].[Customers] AS [t2]

  WHERE ([t1].[CustomerID] = [t2].[CustomerID]) AND ([t2].[ContactName] LIKE @p0)

  ))) AND ([t1].[City] LIKE @p1)

  ORDER BY [t1].[ContactName]

  -- @p0: Input String (Size = 2; Prec = 0; Scale = 0) [A%]

  -- @p1: Input String (Size = 3; Prec = 0; Scale = 0) [%A%]

  子查询

  描述:查询订单数超过5的顾客信息

  查询句法:

 


 var 子查询 = from c in ctx.Customers
  where
  (from o in ctx.Orders group o by o.CustomerID into o where o.Count() > 5 select o.Key).Contains(c.CustomerID)
  select c;

  对应SQL:

  SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax]
  FROM [dbo].[Customers] AS [t0]
  WHERE EXISTS(
  SELECT NULL AS [EMPTY]
  FROM (
  SELECT COUNT(*) AS [value], [t1].[CustomerID]
  FROM [dbo].[Orders] AS [t1]
  GROUP BY [t1].[CustomerID]
  ) AS [t2]
  WHERE ([t2].[CustomerID] = [t0].[CustomerID]) AND ([t2].[value] > @p0)
  )
  -- @p0: Input Int32 (Size = 0; Prec = 0; Scale = 0) [5]

  in操作

  描述:查询指定城市中的客户

  查询句法:

 


 var in操作 = from c in ctx.Customers
  where new string[] { "Brandenburg", "Cowes", "Stavern" }.Contains(c.City)
  select c;

  对应SQL:

  SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], 

[t0].[Country], [t0].[Phone], [t0].[Fax]
  FROM [dbo].[Customers] AS [t0]
  WHERE [t0].[City] IN (@p0, @p1, @p2)
  -- @p0: Input String (Size = 11; Prec = 0; Scale = 0) [Brandenburg]
  -- @p1: Input String (Size = 5; Prec = 0; Scale = 0) [Cowes]
  -- @p2: Input String (Size = 7; Prec = 0; Scale = 0) [Stavern]

  join

  描述:内连接,没有分类的产品查询不到

  查询句法:

  


var innerjoin = from p in ctx.Products
  join c in ctx.Categories
  on p.CategoryID equals c.CategoryID
  select p.ProductName;

  对应SQL:

 


 SELECT COUNT(*) AS [value]
  FROM [dbo].[Products] AS [t0]
  INNER JOIN [dbo].[Categories] AS [t1] ON [t0].[CategoryID] = ([t1].[CategoryID])

  描述:外连接,没有分类的产品也能查询到

  查询句法:

  


var leftjoin = from p in ctx.Products
  join c in ctx.Categories
  on p.CategoryID equals c.CategoryID
  into pro
  from x in pro.DefaultIfEmpty()
  select p.ProductName;

  对应SQL:

  


SELECT COUNT(*) AS [value]
  FROM [dbo].[Products] AS [t0]
  LEFT OUTER JOIN [dbo].[Categories] AS [t1] ON [t0].[CategoryID] = ([t1].[CategoryID])

  你可能会很奇怪,原先很复杂的SQL使用查询句法会很简单(比如按照条件分组)。但是原先觉得很好理解的SQL使用查询句法会觉得很复杂(比如连接查询)。其实,我们还可以通过其它方式进行连接操作,在以后说DataLoadOptions类型的时候会再说。虽然Linq to sql已经非常智能了,但是对于非常复杂的查询还是建议通过存储过程实现,下次讲解如何调用存储过程。

一步一步学Linq to sql(五):存储过程

2007-10-20 00:00 作者: LoveCherry 出处: 天极网 责任编辑:>dizzarz

  普通存储过程

  首先在查询分析器运行下面的代码来创建一个存储过程:

  


create proc sp_singleresultset
  as
  set nocount on
  select * from customers

  然后打开IDE的服务器资源管理器,之前我们从表中拖动表到dbml设计视图,这次我们从存储过程中找到刚才创建的存储过程,然后拖动到设计视图。在方法面板中可以看到已经创建了一个sp_singleresultset的方法,如下图:

  

  然后打开Northwind.designer.cs,可以找到下面的代码:

  


[Function(Name="dbo.sp_singleresultset")]
  public ISingleResult sp_singleresultset()
  {
  IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())));
  return ((ISingleResult)(result.ReturnValue));
  }

  我们可以发现,IDE为这个存储过程单独生成了返回结果集的实体定义,你可能会觉得很奇怪,IDE怎么知道这个存储过程将会返回哪些数据那?其实,在把存储过程拖拽入dbml设计视图的时候,IDE就执行了类似下面的命令:

  


SET FMTONLY ON;
  exec Northwind.dbo.sp_singleresultset
  SET FMTONLY OFF;

  这样就可以直接获取存储过程返回的元数据而无须执行存储过程。

  其实我们存储过程返回的就是顾客表的数据,如果你觉得为存储过程单独设置结果集实体有些浪费的话可以在存储过程的属性窗口中调整返回类型从“自动生成的类型”到Customer,不过以后你只能通过删除方法面板中的存储过程,然后重新添加来还原到“自动生成的类型”。下面,我们可以写如下的Linq to object代码进行查询:

  var 单结果集存储过程 =

  


from c in ctx.sp_singleresultset()
  where c.CustomerID.StartsWith("A")
  select c;

  在这里确实是Linq to object的,因为查询句法不会被整句翻译成SQL,而是从存储过程的返回对象中再去对对象进行查询。SQL代码如下:

  


EXEC @RETURN_VALUE = [dbo].[sp_singleresultset]
  -- @RETURN_VALUE: Output Int32 (Size = 0; Prec = 0; Scale = 0) []

  带参数的存储过程

  创建如下存储过程:

  


create proc [dbo].[sp_withparameter]
  @customerid nchar(5),
  @rowcount int output
  as
  set nocount on
  set @rowcount = (select count(*) from customers where customerid = @customerid)
 

    使用同样的方法生成存储过程方法,然后使用下面的代码进行测试:

  


int? rowcount = -1;
  ctx.sp_withparameter("", ref rowcount);
  Response.Write(rowcount);
  ctx.sp_withparameter("ALFKI", ref rowcount);
  Response.Write(rowcount);

  结果输出了“01”。说明ID为“”的顾客数为0,而ID为“ALFKI”的顾客数为1。存储过程的输出参数被封装成了ref参数,对于C#语法来说非常合情合理。SQL代码如下:

  


EXEC @RETURN_VALUE = [dbo].[sp_withparameter] @customerid = @p0, @rowcount = @p1 OUTPUT
  -- @p0: Input StringFixedLength (Size = 5; Prec = 0; Scale = 0) []
  -- @p1: InputOutput Int32 (Size = 0; Prec = 0; Scale = 0) [-1]
  -- @RETURN_VALUE: Output Int32 (Size = 0; Prec = 0; Scale = 0) []

  带返回值的存储过程

  再来创建第三个存储过程:

  


create proc [dbo].[sp_withreturnvalue]
  @customerid nchar(5)
  as
  set nocount on
  if exists (select 1 from customers where customerid = @customerid)
  return 101
  else
  return 100

  生成方法后,可以通过下面的代码进行测试:

  


Response.Write(ctx.sp_withreturnvalue(""));
  Response.Write(ctx.sp_withreturnvalue("ALFKI"));

  运行后程序输出“100101”

  多结果集的存储过程

  再来创建一个多结果集的存储过程:

 


 create proc [dbo].[sp_multiresultset]
  as
  set nocount on
  select * from customers
  select * from employees

  找到生成的存储过程方法:

 


 [Function(Name="dbo.sp_multiresultset")]
  public ISingleResult sp_multiresultset()
  {
  IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())));
  return ((ISingleResult)(result.ReturnValue));
  }

  由于现在的VS2008会把多结果集存储过程识别为单结果集存储过程(只认识第一个结果集),我们只能对存储过程方法多小动手术,修改为:

  


[Function(Name="dbo.sp_multiresultset")]
  [ResultType(typeof(Customer))]
  [ResultType(typeof(Employee))]
  public IMultipleResults sp_multiresultset()
  {
  IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())));
  return (IMultipleResults)(result.ReturnValue);
  }

  然后使用下面的代码测试:

  


var 多结果集存储过程 = ctx.sp_multiresultset();
  var Customers = 多结果集存储过程.GetResult();
  var Employees = 多结果集存储过程.GetResult();

GridView1.DataSource = from emp in Employees where emp.FirstName.Contains("A") select emp;

 GridView1.DataBind();

  GridView2.DataSource = from c in Customers where c.CustomerID.StartsWith("A") select c;

  GridView2.DataBind();

  

   使用存储过程新增数据

  存储过程除了可以直接调用之外,还可以用于实体的增删改操作。还记得在《一步一步学Linq to sql(三):增删改》中创建的留言簿程序吗?下面我们就来改造这个程序,使用存储过程而不是系统生成的SQL实现实体增删改。首先,我们创建下面的存储过程

  


create proc sendmessage
  @username varchar(50),
  @message varchar(500)
  as
  insert into tbguestbook
  (id,username,posttime,[message],isreplied,reply)
  values
  (newid(),@username,getdate(),@message,0,'')

  然后,打开留言簿dbml,把存储过程从服务器资源管理器拖拽到设计视图上。右键点击tbGuestBook实体类,选择配置行为。如下图,为插入操作选择刚才创建的存储过程方法,并进行参数匹配:

  

  由于我们的存储过程只接受2个参数,相应修改以下创建留言的按钮处理事件:

  


protected void btn_SendMessage_Click(object sender, EventArgs e)
  {
  tbGuestBook gb = new tbGuestBook();
  gb.UserName = tb_UserName.Text;
  gb.Message = tb_Message.Text;
  ctx.tbGuestBooks.Add(gb);
  ctx.SubmitChanges();
  SetBind();
  }

  运行程序后可以发现,在提交修改的时候调用了下面的SQL:

 


 EXEC @RETURN_VALUE = [dbo].[sendmessage] @username = @p0, @message = @p1
  -- @p0: Input AnsiString (Size = 5; Prec = 0; Scale = 0) [zhuye]
  -- @p1: Input AnsiString (Size = 11; Prec = 0; Scale = 0) [new message]
  -- @RETURN_VALUE: Output Int32 (Size = 0; Prec = 0; Scale = 0) []

  使用存储过程删除数据

  创建如下存储过程:

 


 create proc delmessage
  @id uniqueidentifier
  as
  delete tbguestbook where id=@id

  按照前面的步骤生成存储过程方法,并为删除操作执行这个存储过程方法。在选择参数的时候我们可以看到,ID分当前值和原始值,我们选择当前值即可,如下图:

  

  无须改动任何逻辑代码,进行删除留言操作后可以跟踪到下面的SQL:

 


 EXEC @RETURN_VALUE = [dbo].[delmessage] @id = @p0
  -- @p0: Input Guid (Size = 0; Prec = 0; Scale = 0) [9e3c5ee3-2575-458e-899d-4b0bf73e0849]
  -- @RETURN_VALUE: Output Int32 (Size = 0; Prec = 0; Scale = 0) []

  使用存储过程更改数据

  创建如下存储过程:

  


create proc replymessage
  @id uniqueidentifier,
  @reply varchar(500)
  as
  update tbguestbook set reply=@reply,isreplied=1 where id=@id

  由于更新的时候并不会更新主键,所以我们可以为两个参数都指定当前值。回复留言后可以跟踪到下面的SQL:

 


 EXEC @RETURN_VALUE = [dbo].[replymessage] @id = @p0, @reply = @p1
  -- @p0: Input Guid (Size = 0; Prec = 0; Scale = 0) [67a69d0f-a88b-4b22-8939-fed021eb1cb5]
  -- @p1: Input AnsiString (Size = 6; Prec = 0; Scale = 0) [464456]
  -- @RETURN_VALUE: Output Int32 (Size = 0; Prec = 0; Scale = 0) []

  假设有这样一种应用,我们需要修改留言簿中不合法的用户名:

 


 create proc modiusername
  @oldusername varchar(50),
  @newusername varchar(50)
  as
  update tbguestbook set username=@newusername where username = @oldusername

  有个网友起名叫“admin”,我们要把所有这个名字修改为“notadmin”。那么,可以如下图设置update操作:

  

  然后运行下面的测试代码:

  


var messages = from gb in ctx.tbGuestBooks
  select gb;
  foreach (var gb in messages)
  {
  if(gb.UserName == "admin")
  gb.UserName = "notadmin";
  }

  运行程序后能跟踪到下面的SQL:

  


SELECT [t0].[ID], [t0].[UserName], [t0].[PostTime], [t0].[Message], [t0].[IsReplied], [t0].[Reply]
  FROM [dbo].[tbGuestBook] AS [t0]
  EXEC @RETURN_VALUE = [dbo].[modiusername] @oldusername = @p0, @newusername = @p1
  -- @p0: Input AnsiString (Size = 5; Prec = 0; Scale = 0) [admin]
  -- @p1: Input AnsiString (Size = 8; Prec = 0; Scale = 0) [notadmin]
  -- @RETURN_VALUE: Output Int32 (Size = 0; Prec = 0; Scale = 0) []

  到这里,你应该能明白当前值和原始值的含义了吧。

一步一步学Linq to sql(六):探究特性

2007-10-20 09:00 作者: lovecherry 出处: 天极网 责任编辑:>dizzarz

  延迟执行

  


IQueryable query = from c in ctx.Customers select c;

  这样的查询句法不会导致语句立即执行,它仅仅是一个描述,对应一个SQL。仅仅在需要使用的时候才会执行语句,比如:

  


IQueryable query = from c in ctx.Customers select c;
  foreach (Customer c in query)
  Response.Write(c.CustomerID);

  如果你执行两次foreach操作,将会捕获到两次SQL语句的执行:

 


 IQueryable query = from c in ctx.Customers select c;
  foreach (Customer c in query)
  Response.Write(c.CustomerID);
  foreach (Customer c in query)
  Response.Write(c.ContactName);

  对应SQL:

SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax]
  FROM [dbo].[Customers] AS [t0]
  SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax]
  FROM [dbo].[Customers] AS [t0]

  对于这样的需求,建议你先使用ToList()等方法把查询结果先进行保存,然后再对集合进行查询:

  


IEnumerable customers = (from c in ctx.Customers select c).ToList();
  foreach (Customer c in customers)
  Response.Write(c.CustomerID);
  foreach (Customer c in customers)
  Response.Write(c.ContactName);

  延迟执行的优点在于我们可以像拼接SQL那样拼接查询句法,然后再执行:

  


var query = from c in ctx.Customers select c;
  var newquery = (from c in query select c).OrderBy(c => c.CustomerID);
  DataLoadOptions
  var products = from p in ctx.Products select p;
  foreach (var p in products)
  {
  if (p.UnitPrice > 10)
  ShowDetail(p.Order_Details);
  }
  private void ShowDetail(EntitySet orderdetails)
  {}

  由于ShowDetail方法并没有使用到订单详细信息,所以这个操作只会执行下面的SQL:

   SELECT [t0].[ProductID], [t0].[ProductName], [t0].[SupplierID], [t0].[CategoryID], [t0].[QuantityPerUnit], [t0].[UnitPrice], [t0].[UnitsInStock], [t0].[UnitsOnOrder], [t0].[ReorderLevel], [t0] .[Discontinued]
  FROM [dbo].[Products] AS [t0]

  现在修改一下ShowDetail方法:

  


private void ShowDetail(EntitySet orderdetails)
  {
  foreach (var o in orderdetails)
  {
  Response.Write(o.Quantity + "
");
  }
  }

  你会发现Linq to sql对每个价格大于10的产品都根据产品号进行了一次查询:

 


 SELECT [t0].[OrderID], [t0].[ProductID], [t0].[UnitPrice], [t0].[Quantity], [t0].[Discount]
  FROM [dbo].[Order Details] AS [t0]
  WHERE [t0].[ProductID] = @p0
  -- @p0: Input Int32 (Size = 0; Prec = 0; Scale = 0) [1]

  这样的语句查询了N次。这样的查询不是很合理,我们可以通过设置DataContext的DataLoadOption,来指示 DataContext再加载产品信息的同时把对应的产品订单信息一起加载:


  DataLoadOptions options = new DataLoadOptions();
  options.LoadWith(p => p.Order_Details);
  ctx.LoadOptions = options;
  var products = from p in ctx.Products select p;
  。。。。。。。。

  再执行先前的查询会发现Linq to sql进行了左连接:

 


 SELECT [t0].[ProductID], [t0].[ProductName], [t0].[SupplierID], [t0].[CategoryID], [t0].[QuantityPerUnit],

 [t0].[UnitPrice],

 [t0].[UnitsInStock],

 [t0].[UnitsOnOrder], 

[t0].[ReorderLevel], [t0].[Discontinued], [t1].[OrderID],

 [t1].[ProductID] AS [ProductID2], [t1].[UnitPrice] AS [UnitPrice2], [t1].[Quantity], [t1].[Discount], (
  SELECT COUNT(*)
  FROM [dbo].[Order Details] AS [t2]
  WHERE [t2].[ProductID] = [t0].[ProductID]
  ) AS [count]
  FROM [dbo].[Products] AS [t0]
  LEFT OUTER JOIN [dbo].[Order Details] AS [t1] ON [t1].[ProductID] = [t0].[ProductID]
  ORDER BY [t0].[ProductID], [t1].[OrderID]

  那么,我们怎么限制订单详细表的加载条件那?

 


 DataLoadOptions options = new DataLoadOptions();
  options.LoadWith(p => p.Order_Details);
  options.AssociateWith(p => p.Order_Details.Where(od => od.Quantity > 80));
  ctx.LoadOptions = options;
  var products = from p in ctx.Products select p;

  这样,就只会有数量大于80的订单详细信息会和产品一起加载:

 


 SELECT [t0].[ProductID], [t0].[ProductName], [t0].[SupplierID], [t0].[CategoryID], 

[t0].[QuantityPerUnit], [t0].[UnitPrice], 

[t0].[UnitsInStock],

 [t0].[UnitsOnOrder], 

[t0].[ReorderLevel], [t0].[Discontinued], [t1].[OrderID],

 [t1].[ProductID] AS [ProductID2], [t1].[UnitPrice] AS [UnitPrice2], [t1].[Quantity], [t1].[Discount], (
  SELECT COUNT(*)
  FROM [dbo].[Order Details] AS [t2]
  WHERE ([t2].[Quantity] > @p0) AND ([t2].[ProductID] = [t0].[ProductID])
  ) AS [count]
  FROM [dbo].[Products] AS [t0]
  LEFT OUTER JOIN [dbo].[Order Details] AS [t1] ON ([t1].[Quantity] > @p0) 

AND ([t1].[ProductID] = [t0].[ProductID])
  ORDER BY [t0].[ProductID], [t1].[OrderID]
  -- @p0: Input Int32 (Size = 0; Prec = 0; Scale = 0) [80]
  DataLoadOptions限制
  Linq to sql对DataLoadOptions的使用是有限制的,它只支持1个1对多的关系。一个顾客可能有多个订单,一个订单可能有多个详细订单:
  DataLoadOptions options = new DataLoadOptions();
  options.LoadWith(c => c.Orders);
  options.LoadWith(o => o.Order_Details);
  ctx.LoadOptions = options;
  IEnumerable customers = ctx.Customers.ToList();

  这样的语句执行后会导致下面的SQL执行N次(参数不同):

 


 SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[RequiredDate], [t0].[ShippedDate],

 [t0].[ShipVia], 

[t0].[Freight], [t0].[ShipName],

 [t0].[ShipAddress], [t0].[ShipCity], [t0].[ShipRegion],

 [t0].[ShipPostalCode], [t0].[ShipCountry], [t1].[OrderID] AS [OrderID2], [t1].[ProductID], [t1].[UnitPrice], 

[t1].[Quantity],

 [t1].[Discount], (
  SELECT COUNT(*)
  FROM [dbo].[Order Details] AS [t2]
  WHERE [t2].[OrderID] = [t0].[OrderID]
  ) AS [count]
  FROM [dbo].[Orders] AS [t0]
  LEFT OUTER JOIN [dbo].[Order Details] AS [t1] ON [t1].[OrderID] = [t0].[OrderID]
  WHERE [t0].[CustomerID] = @x1
  ORDER BY [t0].[OrderID], [t1].[ProductID]
  -- @x1: Input StringFixedLength (Size = 5; Prec = 0; Scale = 0) [ALFKI]
  

     而对于多对1的关系,Linq to sql对于DataLoadOptions没有限制:  

   


  DataLoadOptions options = new DataLoadOptions();
  options.LoadWith(c => c.Category);
  options.LoadWith(c => c.Order_Details);
  options.LoadWith(o => o.Order);
  ctx.LoadOptions = options;
  IEnumerable products = ctx.Products.ToList();

  由于多个产品对应1个分类,多个详细订单对应1个订单,只有产品和详细订单才是多对1的关系,所以也只会有1次SQL(不过这样的操作还是少执行为妙,消耗太大了):

 


 SELECT [t0].[ProductID], [t0].[ProductName], [t0].[SupplierID], [t0].[CategoryID], [t0].[QuantityPerUnit], 

[t0].[UnitPrice], 

[t0].[UnitsInStock],

 [t0].[UnitsOnOrder], [t0].[ReorderLevel], [t0].[Discontinued], 

[t3].[OrderID], [t3].[ProductID] AS [ProductID2], [t3].[UnitPrice] AS [UnitPrice2], [t3].[Quantity], [t3].[Discount],

 [t4].[OrderID]

 AS [OrderID2], 

[t4].[CustomerID], [t4].[EmployeeID], [t4].[OrderDate],

 [t4].[RequiredDate], [t4].[ShippedDate], [t4].[ShipVia], [t4].[Freight], [t4].[ShipName], [t4].[ShipAddress], 

[t4].[ShipCity],

 [t4].[ShipRegion],

 [t4].[ShipPostalCode], [t4].[ShipCountry], (
  SELECT COUNT(*)
  FROM [dbo].[Order Details] AS [t5]
  INNER JOIN [dbo].[Orders] AS [t6] ON [t6].[OrderID] = [t5].[OrderID]
  WHERE [t5].[ProductID] = [t0].[ProductID]
  ) AS [count], [t2].[test], [t2].[CategoryID] AS [CategoryID2], [t2].[CategoryName], [t2].[Description], [t2].[Picture]
  FROM [dbo].[Products] AS [t0]
  LEFT OUTER JOIN (
  SELECT 1 AS [test], [t1].[CategoryID], [t1].[CategoryName], [t1].[Description], [t1].[Picture]
  FROM [dbo].[Categories] AS [t1]
  ) AS [t2] ON [t2].[CategoryID] = [t0].[CategoryID]
  LEFT OUTER JOIN ([dbo].[Order Details] AS [t3]
  INNER JOIN [dbo].[Orders] AS [t4] ON [t4].[OrderID] = [t3].[OrderID]) ON [t3].[ProductID] = [t0].[ProductID]
  ORDER BY [t0].[ProductID], [t2].[CategoryID], [t3].[OrderID]

  主键缓存

  Linq to sql对查询过的对象进行缓存,之后的如果只根据主键查询一条记录的话会直接从缓存中读取。比如下面的代码:

 


 Customer c1 = ctx.Customers.Single(customer => customer.CustomerID == "ANATR");
  c1.ContactName = "zhuye";
  Customer c2 = ctx.Customers.Single(customer => customer.CustomerID == "ANATR");
  Response.Write(c2.ContactName);

  执行后只会产生一条SQL:


  SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], 

[t0].[Address], [t0].[City], [t0].[Region],

 [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax]
  FROM [dbo].[Customers] AS [t0]
  WHERE [t0].[CustomerID] = @p0
  -- @p0: Input String (Size = 5; Prec = 0; Scale = 0) [ANATR]

  由于没有提交修改,所以数据库中的记录还是没有更新。由于这个特性,我们在使用存储过程作为实体更新方法的时候就要当心了,存储过程书写错误,即使你提交了修改也很可能导致缓存中的数据和数据库中的数据不一致,引起不必要的麻烦。

  DataContext隔离

  有的时候我们会把对象从外部传入DataContext,要求它更新,由于不同的DataContext是相对独立的。由于新的DataContext中还没有获取实体,我们只能通过附加方式更新数据。

  首先把Customer表的主键字段加上IsVersion标识:

  


[Column(Storage="_CustomerID", DbType="NChar(5) NOT NULL",

 CanBeNull=false, IsPrimaryKey=true, IsVersion = true)]

  运行下面的测试代码:

  


Customer c = new Customer { CustomerID = "ALFKI", ContactName = "zhuye", CompanyName = "1111" };
  ctx.Customers.Attach(c, true);
  ctx.SubmitChanges();

  会捕捉到下面的SQL语句:

 


 UPDATE [dbo].[Customers]
  SET [CompanyName] = @p2, [ContactName] = @p3, [ContactTitle] = @p4,

 [Address] = @p5, [City] = @p6, [Region] = @p7, [PostalCode] = @p8, [Country] = @p9, [Phone] = @p10, 

[Fax] = @p11
  WHERE ([CustomerID] = @p0) AND ([CustomerID] = @p1)
  -- @p0: Input StringFixedLength (Size = 5; Prec = 0; Scale = 0) [ALFKI]
  -- @p1: Input String (Size = 5; Prec = 0; Scale = 0) [ALFKI]
  -- @p2: Input String (Size = 4; Prec = 0; Scale = 0) [1111]
  -- @p3: Input String (Size = 5; Prec = 0; Scale = 0) [zhuye]
  -- @p4: Input String (Size = 0; Prec = 0; Scale = 0) []
  -- @p5: Input String (Size = 0; Prec = 0; Scale = 0) []
  -- @p6: Input String (Size = 0; Prec = 0; Scale = 0) []
  -- @p7: Input String (Size = 0; Prec = 0; Scale = 0) []
  -- @p8: Input String (Size = 0; Prec = 0; Scale = 0) []
  -- @p9: Input String (Size = 0; Prec = 0; Scale = 0) []
  -- @p10: Input String (Size = 0; Prec = 0; Scale = 0) []
  -- @p11: Input String (Size = 0; Prec = 0; Scale = 0) []

  今天就到这里,下次讲并发与事务问题。

一步一步学Linq to sql(七):并发与事务

2007-10-20 09:00 作者: lovecherry 出处: 天极网 责任编辑:>dizzarz

  检测并发

  首先使用下面的SQL语句查询数据库的产品表:

  


select * from products where categoryid=1

  查询结果如下图:

  

  为了看起来清晰,我已经事先把所有分类为1产品的价格和库存修改为相同值了。然后执行下面的程序:

 


 var query = from p in ctx.Products where p.CategoryID == 1 select p;
  foreach (var p in query)
  p.UnitsInStock = Convert.ToInt16(p.UnitsInStock - 1);
  ctx.SubmitChanges(); // 在这里设断点

  我们使用调试方式启动,由于设置了断点,程序并没有进行更新操作。此时,我们在数据库中运行下面的语句:

 


 update products
  set unitsinstock = unitsinstock -2, unitprice= unitprice + 1
  where categoryid = 1

  然后在继续程序,会得到修改并发(乐观并发冲突)的异常,提示要修改的行不存在或者已经被改动。当客户端提交的修改对象自读取之后已经在数据库中发生改动,就产生了修改并发。解决并发的包括两步,一是查明哪些对象发生并发,二是解决并发。如果你仅仅是希望更新时不考虑并发的话可以关闭相关列的更新验证,这样在这些列上发生并发就不会出现异常:


  [Column(Storage="_UnitsInStock", DbType="SmallInt", UpdateCheck = UpdateCheck.Never)]
  [Column(Storage="_UnitPrice", DbType="Money", UpdateCheck = UpdateCheck.Never)]

  为这两列标注不需要进行更新检测。假设现在产品价格和库存分别是27和32。那么,我们启动程序(设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别是28和31。价格+1是之前更新的功劳,库存最终是-1是我们程序之后更新的功劳。当在同一个字段上(库存)发生并发冲突的时候,默认是最后的那次更新获胜。

  解决并发

  如果你希望自己处理并发的话可以把前面对列的定义修改先改回来,看下面的例子:

 


 var query = from p in ctx.Products where p.CategoryID == 1 select p;
  foreach (var p in query)
  p.UnitsInStock = Convert.ToInt16(p.UnitsInStock - 1);
  try
  {
  ctx.SubmitChanges(ConflictMode.ContinueOnConflict);
  }
  catch (ChangeConflictException)
  {
  foreach (ObjectChangeConflict cc in ctx.ChangeConflicts)
  {
  Product p = (Product)cc.Object;
  Response.Write(p.ProductID + "
");
  cc.Resolve(RefreshMode.OverwriteCurrentValues); // 放弃当前更新,所有更新以原先更新为准
  }
  }
  ctx.SubmitChanges();

  首先可以看到,我们使用try{}catch{}来捕捉并发冲突的异常。在SubmitChanges的时候,我们选择了ConflictMode.ContinueOnConflict选项。也就是说遇到并发了还是继续。在catch{}中,我们从ChangeConflicts中获取了并发的对象,然后经过类型转化后输出了产品ID,然后选择的解决方案是RefreshMode.OverwriteCurrentValues。也就是说,放弃当前的更新,所有更新以原先更新为准。(源码网整理:www.codepub.com)

  我们来测试一下,假设现在产品价格和库存分别是27和32。那么,我们启动程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)这里设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别是28和30。之前SQL语句库存-2生效了,而我们程序的更新(库存-1)被放弃了。在页面上也显示了所有分类为1的产品ID(因为我们之前的SQL语句是对所有分类为1的产品都进行修改的)。

  然后,我们来修改一下解决并发的方式:

 


 cc.Resolve(RefreshMode.KeepCurrentValues); // 放弃原先更新,所有更新以当前更新为准

  来测试一下,假设现在产品价格和库存分别是27和32。那么,我们启动程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)这里设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别是27和31。产品价格没有变化,库存-1了,都是我们程序的功劳,SQL语句的更新被放弃了。

  然后,我们再来修改一下解决并发的方式:


  cc.Resolve(RefreshMode.KeepChanges); // 原先更新有效,冲突字段以当前更新为准

  来测试一下,假设现在产品价格和库存分别是27和32。那么,我们启动程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)这里设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别是28和31。这就是默认方式,在保持原先更新的基础上,对于发生冲突的字段以最后更新为准。

  我们甚至还可以针对不同的字段进行不同的处理策略:

  


foreach (ObjectChangeConflict cc in ctx.ChangeConflicts)
  {
  Product p = (Product)cc.Object;
  foreach (MemberChangeConflict mc in cc.MemberConflicts)
  {
  string currVal = mc.CurrentValue.ToString();
  string origVal = mc.OriginalValue.ToString();
  string databaseVal = mc.DatabaseValue.ToString();
  MemberInfo mi = mc.Member;
  string memberName = mi.Name;
  Response.Write(p.ProductID + " " + mi.Name + " " + currVal + " " + origVal +" "+ databaseVal + "
");
  if (memberName == "UnitsInStock")
  mc.Resolve(RefreshMode.KeepCurrentValues); // 放弃原先更新,所有更新以当前更新为准
  else if (memberName == "UnitPrice")
  mc.Resolve(RefreshMode.OverwriteCurrentValues); // 放弃当前更新,所有更新以原先更新为准
  else
  mc.Resolve(RefreshMode.KeepChanges); // 原先更新有效,冲突字段以当前更新为准
  }
  }

  比如上述代码就对库存字段作放弃原先更新处理,对价格字段作放弃当前更新处理。我们来测试一下,假设现在产品价格和库存分别是27和32。那么,我们启动程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)这里设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别为28和31了。说明对价格的处理确实保留了原先的更新,对库存的处理保留了当前的更新。页面上显示的结果如下图:

  

  最后,我们把提交语句修改为:

  


ctx.SubmitChanges(ConflictMode.FailOnFirstConflict);

  表示第一次发生冲突的时候就不再继续了,然后并且去除最后的ctx.SubmitChanges();语句。来测试一下,在执行了SQL后再继续程序可以发现界面上只输出了数字1,说明在第一条记录失败后,后续的并发冲突就不再处理了。

  事务处理

  Linq to sql在提交更新的时候默认会创建事务,一部分修改发生错误的话其它修改也不会生效:

 


 ctx.Customers.Add(new Customer { CustomerID = "abcdf", CompanyName = "zhuye" });
  ctx.Customers.Add(new Customer { CustomerID = "abcde", CompanyName = "zhuye" });
  ctx.SubmitChanges();

  假设数据库中已经存在顾客ID为“abcde”的记录,那么第二次插入操作失败将会导致第一次的插入操作失效。执行程序后会得到一个异常,查询数据库发现“abcdf”这个顾客也没有插入到数据库中。

  如果每次更新后直接提交修改,那么我们可以使用下面的方式做事务:

  


if (ctx.Connection != null) ctx.Connection.Open();
  DbTransaction tran = ctx.Connection.BeginTransaction();
  ctx.Transaction = tran;
  try
  {
  CreateCustomer(new Customer { CustomerID = "abcdf", CompanyName = "zhuye" });
  CreateCustomer(new Customer { CustomerID = "abcde", CompanyName = "zhuye" });
  tran.Commit();
  }
  catch
  {
  tran.Rollback();
  }
  private void CreateCustomer(Customer c)
  {
  ctx.Customers.Add(c);
  ctx.SubmitChanges();
  }

  运行程序后发现增加顾客abcdf的操作并没有成功。或者,我们还可以通过TransactionScope实现事务:

 


 using (TransactionScope scope = new TransactionScope())
  {
  CreateCustomer(new Customer { CustomerID = "abcdf", CompanyName = "zhuye" });
  CreateCustomer(new Customer { CustomerID = "abcde", CompanyName = "zhuye" });
  scope.Complete();
  }

一步一步学Linq to sql(八):继承与关系

2007-10-20 00:00 作者: LoveCherry 出处: 天极网 责任编辑:>dizzarz

        论坛表结构

       为了演示继承与关系,我们创建一个论坛数据库,在数据库中创建三个表:

       1、  论坛版块分类表 dbo.Categories:

字段名

字段类型

可空

备注

CategoryID

int

not null

identity/主键

CategoryName

varchar(50)

not null

      2、  论坛版块表 dbo.Boards:

 

字段名

字段类型

可空

备注

BoardID

int

not null

identity/主键

BoardName

varchar(50)

not null

BoardCategory

int

not null

对应论坛版块分类表的CategoryID

     3、  论坛主题表 dbo.Topics:

字段名

字段类型

可空

备注

TopicID

int

not null

identity/主键

TopicTitle

varchar(50)

not null

TopicContent

varchar(max)

not null

ParentTopic

int

null

如果帖子是主题贴这个字段为null,否则就是所属主题id

TopicType

tinyint

not null

0 – 主题贴

1 – 回复帖

      实体继承的定义

  Linq to sql支持实体的单表继承,也就是基类和派生类都存储在一个表中。对于论坛来说,帖子有两种,一种是主题贴,一种是回复帖。那么,我们就先定义帖子基类:

 


 [Table(Name = "Topics")]
  public class Topic
  {
  [Column(Name = "TopicID", DbType = "int identity", IsPrimaryKey = true, IsDbGenerated = true, CanBeNull = false)]
  public int TopicID { get; set; }
  [Column(Name = "TopicType", DbType = "tinyint", CanBeNull = false)]
  public int TopicType { get; set; }
  [Column(Name = "TopicTitle", DbType = "varchar(50)", CanBeNull = false)]
  public string TopicTitle { get; set; }
  [Column(Name = "TopicContent", DbType = "varchar(max)", CanBeNull = false)]
  public string TopicContent { get; set; }
  }

  这些实体的定义大家应该很熟悉了。下面,我们再来定义两个实体继承帖子基类,分别是主题贴和回复贴:

  


public class NewTopic : Topic
  {
  public NewTopic()
  {
  base.TopicType = 0;
  }
  }
  public class Reply : Topic
  {
  public Reply()
  {
  base.TopicType = 1;
  }
  [Column(Name = "ParentTopic", DbType = "int", CanBeNull = false)]
  public int ParentTopic { get; set; }
  }

  对于主题贴,在数据库中的TopicType就保存为0,而对于回复贴就保存为1。回复贴还有一个相关字段就是回复所属主题贴的TopicID。那么,我们怎么告知Linq to sql在TopicType为0的时候识别为NewTopic,而1则识别为Reply那?只需稍微修改一下前面的Topic实体定义:

 


 [Table(Name = "Topics")]
  [InheritanceMapping(Code = 0, Type = typeof(NewTopic), IsDefault = true)]
  [InheritanceMapping(Code = 1, Type = typeof(Reply))]
  public class Topic
  {
  [Column(Name = "TopicID", DbType = "int identity", IsPrimaryKey = true, IsDbGenerated = true, CanBeNull = false)]
  public int TopicID { get; set; }
  [Column(Name = "TopicType", DbType = "tinyint", CanBeNull = false, IsDiscriminator = true)]
  public int TopicType { get; set; }
  [Column(Name = "TopicTitle", DbType = "varchar(50)", CanBeNull = false)]
  public string TopicTitle { get; set; }
  [Column(Name = "TopicContent", DbType = "varchar(max)", CanBeNull = false)]
  public string TopicContent { get; set; }
  }

  为类加了InheritanceMapping特性定义,0的时候类型就是NewTopic,1的时候就是Reply。并且为TopicType字段上的特性中加了IsDiscriminator = true,告知Linq to sql这个字段就是用于分类的字段。

  实体继承的使用

  定义好继承的实体之后,我们就可以使用了。先是自定义一个DataContext吧:

 


 public partial class BBSContext : DataContext
  {
  public Table BoardCategories;
  public Table Boards;
  public Table Topics;
  public BBSContext(string connection) : base(connection) { }
  }

  然后,我们来测试一下Linq to sql是否能根据TopicType识别派生类:

 


 BBSContext ctx = new BBSContext("server=xxx;database=BBS;uid=xxx;pwd=xxx");
  var query = from t in ctx.Topics select t;
  foreach (Topic topic in query)
  {
  if (topic is NewTopic)
  {
  NewTopic newtopic = topic as NewTopic;
  Response.Write("标题:" + newtopic.TopicTitle + " 类型:" + newtopic.TopicType + "
");
  }

else if (topic is Reply)

  {

  Reply reply = topic as Reply;

  Response.Write("标题:" + reply.TopicTitle + " 类型:" + reply.TopicType + " 隶属主题:" + reply.ParentTopic + "
");

  }

  }

    然后我们往Topics表中加一些数据,如下图:

  

  启动程序得到如下测试结果:

  

  当然,你也可以在查询句法中直接查询派生实体:

  


IEnumerable newtopiclist = (from t in ctx.Topics.OfType() select t).ToList();
  newtopics.DataSource = newtopiclist;
  IEnumerable replylist = (from t in ctx.Topics.OfType() select t).ToList();
  replies.DataSource = replylist;
  Page.DataBind();

  newtopic和replies是两个GridView控件,执行效果如下图:

  

  再来看看如何进行增删操作:

  


NewTopic nt = new NewTopic() { TopicTitle = "还是新主题", TopicContent = "还是新主题" };
  Reply rpl = new Reply() { TopicTitle = "还是新回复", TopicContent = "还是新回复", ParentTopic = 4 };
  ctx.Topics.Add(nt);
  ctx.Topics.Add(rpl);
  ctx.SubmitChanges();
  rpl = ctx.Topics.OfType().Single(reply => reply.TopicID == 8);
  ctx.Topics.Remove(rpl);
  ctx.SubmitChanges();

  实体关系的定义

  比如我们的论坛分类表和论坛版块表之间就有关系,这种关系是1对多的关系。也就是说一个论坛分类可能有多个论坛版块,这是很常见的。定义实体关系的优势在于,我们无须显式作连接操作就能处理关系表的条件。

  首先来看看分类表的定义:

  


[Table(Name = "Categories")]
  public class BoardCategory
  {
  [Column(Name = "CategoryID", DbType = "int identity", IsPrimaryKey = true, IsDbGenerated = true, 

CanBeNull = false)]
  public int CategoryID { get; set; }
  [Column(Name = "CategoryName", DbType = "varchar(50)", CanBeNull = false)]
  public string CategoryName { get; set; }
  private EntitySet _Boards;
  [Association(OtherKey = "BoardCategory", Storage = "_Boards")]
  public EntitySet Boards
  {
  get { return this._Boards; }
  set { this._Boards.Assign(value); }
  }
  public BoardCategory()
  {
  this._Boards = new EntitySet();
  }
  }

  CategoryID和CategoryName的映射没有什么不同,只是我们还增加了一个Boards属性,它返回的是Board实体集。通过特性,我们定义了关系外键为BoardCategory(Board表的一个字段)。然后来看看1对多,多端版块表的实体:

  


[Table(Name = "Boards")]
  public class Board
  {
  [Column(Name = "BoardID", DbType = "int identity", IsPrimaryKey = true, IsDbGenerated = true, CanBeNull = false)]
  public int BoardID { get; set; }
  [Column(Name = "BoardName", DbType = "varchar(50)", CanBeNull = false)]
  public string BoardName { get; set; }
  [Column(Name = "BoardCategory", DbType = "int", CanBeNull = false)]
  public int BoardCategory { get; set; }
  private EntityRef _Category;
  [Association(ThisKey = "BoardCategory", Storage = "_Category")]
  public BoardCategory Category
  {
  get { return this._Category.Entity; }
  set
  {
  this._Category.Entity = value;
  value.Boards.Add(this);
  }
  }
  }

  在这里我们需要关联分类,设置了Category属性使用BoardCategory字段和分类表关联。

  实体关系的使用

  好了,现在我们就可以在查询句法中直接关联表了(数据库中不一定要设置表的外键关系):

  


Response.Write("-------------查询分类为1的版块-------------
");
  var query1 = from b in ctx.Boards where b.Category.CategoryID == 1 select b;
  foreach (Board b in query1)
  Response.Write(b.BoardID + " " + b.BoardName + "
");
  Response.Write("-------------查询版块大于2个的分类-------------
");
  var query2 = from c in ctx.BoardCategories where c.Boards.Count > 2 select c;
  foreach (BoardCategory c in query2)
  Response.Write(c.CategoryID + " " + c.CategoryName + " " + c.Boards.Count + "
");

  在数据库中加一些测试数据,如下图:

  

  运行程序后得到下图的结果:

  

  我想定义实体关系的方便我不需要再用语言形容了吧。执行上述的程序会导致下面SQL的执行:

  


SELECT [t0].[BoardID], [t0].[BoardName], [t0].[BoardCategory]
  FROM [Boards] AS [t0]
  INNER JOIN [Categories] AS [t1] ON [t1].[CategoryID] = [t0].[BoardCategory]
  WHERE [t1].[CategoryID] = @p0
  -- @p0: Input Int32 (Size = 0; Prec = 0; Scale = 0) [1]
  SELECT [t0].[CategoryID], [t0].[CategoryName]
  FROM [Categories] AS [t0]
  WHERE ((
  SELECT COUNT(*)
  FROM [Boards] AS [t1]
  WHERE [t1].[BoardCategory] = [t0].[CategoryID]
  )) > @p0
  -- @p0: Input Int32 (Size = 0; Prec = 0; Scale = 0) [2]
  SELECT [t0].[BoardID], [t0].[BoardName], [t0].[BoardCategory]
  FROM [Boards] AS [t0]
  WHERE [t0].[BoardCategory] = @p0
  -- @p0: Input Int32 (Size = 0; Prec = 0; Scale = 0) [1]

  可以看到,第二个查询并没有做外连接,还记得DataLoadOptions吗?我们可以要求Linq to sql在读取版块分类信息的时候也把版块信息一起加载:

  


DataLoadOptions options = new DataLoadOptions();
  options.LoadWith(c => c.Boards);
  ctx.LoadOptions = options;
  Response.Write("-------------查询版块大于2个的分类-------------
");
  var query2 = from c in ctx.BoardCategories where c.Boards.Count > 2 select c;
  foreach (BoardCategory c in query2)
  Response.Write(c.CategoryID + " " + c.CategoryName + " " + c.Boards.Count + "
");

  查询经过改造后会得到下面的SQL:

  


SELECT [t0].[CategoryID], [t0].[CategoryName], [t1].[BoardID], [t1].[BoardName], [t1].[BoardCategory], (
  SELECT COUNT(*)
  FROM [Boards] AS [t3]
  WHERE [t3].[BoardCategory] = [t0].[CategoryID]
  ) AS [count]
  FROM [Categories] AS [t0]
  LEFT OUTER JOIN [Boards] AS [t1] ON [t1].[BoardCategory] = [t0].[CategoryID]
  WHERE ((
  SELECT COUNT(*)
  FROM [Boards] AS [t2]
  WHERE [t2].[BoardCategory] = [t0].[CategoryID]
  )) > @p0
  ORDER BY [t0].[CategoryID], [t1].[BoardID]
  -- @p0: Input Int32 (Size = 0; Prec = 0; Scale = 0) [2]

  在添加分类的时候,如果这个分类下还有新的版块,那么提交新增分类的时候版块也会新增:

 


 BoardCategory dbcat = new BoardCategory() { CategoryName = "Database" };
  Board oracle = new Board() { BoardName = "Oracle", Category = dbcat};
  ctx.BoardCategories.Add(dbcat);
  ctx.SubmitChanges();

  上述代码导致下面的SQL被执行:

  


INSERT INTO [Categories]([CategoryName]) VALUES (@p0)
  SELECT [t0].[CategoryID]
  FROM [Categories] AS [t0]
  WHERE [t0].[CategoryID] = (SCOPE_IDENTITY())
  -- @p0: Input AnsiString (Size = 8; Prec = 0; Scale = 0) [Database]
  INSERT INTO [Boards]([BoardName], [BoardCategory]) VALUES (@p0, @p1)
  SELECT [t0].[BoardID]
  FROM [Boards] AS [t0]
  WHERE [t0].[BoardID] = (SCOPE_IDENTITY())
  -- @p0: Input AnsiString (Size = 6; Prec = 0; Scale = 0) [Oracle]
  -- @p1: Input Int32 (Size = 0; Prec = 0; Scale = 0) [23]

  今天就讲到这里。

一步一步学Linq to sql(九):其它补充

2007-10-20 09:00 作者: LoveCherry 出处: 天极网 责任编辑:>dizzarz

  外部映射文件

  我们可以使用sqlmetal命令行工具来生成外部映射文件,使用方法如下:

  1、开始菜单 -》 VS2008 -》VS工具 -》VS2008命令行提示

  2、输入命令:

  


D:\Program Files\Microsoft Visual Studio 9.0\VC>sqlmetal /conn:server=xxx;
  database=Northwind;uid=xxx;pwd=xxx /map:c:\northwind.map /code:c:\northwind.cs

  3、这样,我们就可以在C盘下得到一个xml映射文件和C#的实体类代码

  4、把.cs文件添加到项目中来(放到App_Code目录),然后使用下面的代码加载映射文件:

 


 String path = @"C:\Northwind.map";
  XmlMappingSource xms = XmlMappingSource.FromXml(File.ReadAllText(path));
  Northwind ctx = new Northwind("server=xxx;database=Northwind;uid=xxx;pwd=xxx", xms);

  5、现在就可以照常进行其它工作了。使用sqlmetal可以很方便的同步数据库与实体和映射文件。每次修改数据库结构,从dbml设计器上删除表、存储过程然后再重新添加也是很麻烦的事情。

  处理空值

  


var count = (from c in ctx.Customers where c.Region == null select c).Count();
  Response.Write(count + "
  ");
  var query = from emp in ctx.Employees select emp.ReportsTo;
  foreach (Nullable r in query)
  {
  Response.Write(r.HasValue ? r.Value.ToString() + "
  " : "没有
  ");
  }

  代码执行后捕获到下面的SQL被执行:

  


SELECT COUNT(*) AS [value]
  FROM [dbo].[Customers] AS [t0]
  WHERE [t0].[Region] IS NULL
  SELECT [t0].[ReportsTo]
  FROM [dbo].[Employees] AS [t0]

  已编译查询

  对于一些在项目中经常被用到的查询可以封装成已编译查询,这样就能提高执行效率:

  


static class Queries
  {
  public static Func>
  CustomersByCity = CompiledQuery.Compile((NorthwindDataContext ctx, string city) => 

from c in ctx. Customers where c.City == city select c);


  }

  调用查询方式如下:

  


GridView1.DataSource = Queries.CustomersByCity(ctx, "London");
  GridView1.DataBind();

  获取一些信息

  


var query = from c in ctx.Customers select c;
  Response.Write("Provider类型:" + ctx.Mapping.ProviderType + "
  ");
  Response.Write("数据库:" + ctx.Mapping.DatabaseName + "
  ");
  Response.Write("表:" + ctx.Mapping.GetTable(typeof(Customer)).TableName + "
  ");
  Response.Write("表达式:" + query.Expression.ToString() + "
  ");
  Response.Write("sql:" + query.Provider.ToString() + "
  ");

  上面的代码执行结果如下:

 


 Provider类型:System.Data.Linq.SqlClient.SqlProvider
  数据库:Northwind
  表:dbo.Customers
  表达式:Table(Customer).Select(c => c)
  sql:SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address],

 [t0].[City], 

[t0].[Region],

 [t0].[PostalCode], 

[t0].[Country], [t0].[Phone], [t0].[Fax] FROM [dbo].[Customers] AS [t0]窗体顶端

  窗体底端

  撤销提交

 


 var customer = ctx.Customers.Single(c => c.CustomerID == "AROUT");
  customer.ContactName = "zhuye";
  customer.Country = "Shanghai";
  Response.Write(string.Format("Name:{0},Country:{1}
  ", customer.ContactName, customer.Country));
  customer = ctx.Customers.GetOriginalEntityState(customer);
  Response.Write(string.Format("Name:{0},Country:{1}
  ", customer.ContactName, customer.Country));

  上面的代码执行效果如下:

  


Name:zhuye,Country:Shanghai
  Name:Thomas Hardy,Country:UK

  批量操作

  下面的代码会导致提交N次DELETE操作:

  


var query = from c in ctx.Customers select c;
  ctx.Customers.RemoveAll(query);
  ctx.SubmitChanges();

  应该使用sql语句进行批操作:

  


string sql = String.Format("delete from {0}", ctx.Mapping.GetTable(typeof(Customer)).TableName);
  ctx.ExecuteCommand(sql);

  对于批量更新操作也是同样道理。

一步一步学Linq to sql(十):分层构架的例子

2007-10-31 00:00 作者: lovecherry 出处: 天极网 责任编辑:>dizzarz

  项目介绍

  这节将要把《一步一步学Linq to sql(三):增删改》中留言簿的例子修改为使用WCF的多层构架。我们将会建立以下项目:

  •    A,网站项目 WebSite:留言簿表现层
  •    B,类库项目 Contract:定义数据访问服务的契约
  •    C,类库项目 Service:定义数据访问服务
  •    D,类库项目Entity:留言簿实体
  •    E,控制台项目Host:承载数据访问服务

  项目之间的引用如下:

  •    A引用B和D;
  •    B引用D和System.ServiceModel程序集
  •    C引用B、D、System.ServiceModel以及System.Data.Linq程序集
  •    D引用System.Data.Linq程序集
  •    E引用C和System.ServiceModel程序集

  生成映射文件和实体

  打开VS2008命令行提示,执行以下命令:

 


 sqlmetal /conn:server=xxx;

database=GuestBook;uid=xxx;

pwd=xxx /map:c:\guestbook.map /code:c:\guestbook.cs /serialization:Unidirectional

  注意到,这里我们使用了serialization开关,告知sqlmetal在生成实体的时候自动把它们标记为WCF数据对象。生成结束后把C:\GUESTBOOK.CS添加到Entity项目中。

  编写数据访问服务

  首先我们可以定义出留言簿数据访问服务的契约(接口),把如下的代码保存为IDataAccess.cs放在Contract类库项目中:

 


 using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using System.ServiceModel;
  namespace Contract
  {
  [ServiceContract]
  public interface IDataAccess
  {
  [OperationContract]
  void SendMessage(TbGuestBook gb);
  [OperationContract]
  List GetData();
  [OperationContract]
  void DeleteMessage(string ID);
  [OperationContract]
  void SendReply(TbGuestBook gb);
  }
  }

  在这里定义了四个方法:

  •    创建留言
  •   获取所有留言
  •    删除留言
  •    管理员发表回复

  然后,我们来实现这个契约,把如下代码保存为DataAccess.cs放在Service类库项目中:

  


using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using Contract;
  using System.Data.Linq.Mapping;
  using System.IO;
  using System.ServiceModel;
  namespace Service
  {
  [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
  public class DataAccess : IDataAccess
  {
  GuestBook ctx;
  public DataAccess()
  {
  XmlMappingSource xms = XmlMappingSource.FromXml(File.ReadAllText("c:\\guestbook.map"));
  ctx = new GuestBook("server=srv-devdbhost;database=GuestBook;uid=sa;pwd=Abcd1234", xms);
  ctx.Log = Console.Out;
  }
  public void SendMessage(TbGuestBook gb)
  {
  ctx.TbGuestBook.Add(gb);
  ctx.SubmitChanges();
  }
  public List GetData()
  {
  var query = from gb in ctx.TbGuestBook orderby gb.PostTime descending select gb;
  return query.ToList();
  }
  public void DeleteMessage(string ID)
  {
  TbGuestBook gb = ctx.TbGuestBook.Single(message => message.ID == new Guid(ID));
  ctx.TbGuestBook.Remove(gb);
  ctx.SubmitChanges();
  }
  public void SendReply(TbGuestBook gb)
  {
  //ctx.ExecuteCommand("update tbGuestBook set reply={0},isreplied=1 where ID={1}", gb.Reply, gb.ID);
  TbGuestBook record = ctx.TbGuestBook.Single(message => message.ID == gb.ID);
  record.IsReplied = true;
  record.Reply = gb.Reply;
  ctx.SubmitChanges();
  }
  }
  }

  这里需要注意几点:

  •    我们把DataContext的操作在控制台输出
  •    在进行发表回复(更新操作)的时候,注释的代码和没有注释的代码虽然都能完成更新操作,但是前者更合理,因为后者会先进行SELECT再进行UPDATE

  WCF服务端与客户端

  打开Host项目中的Program.cs,使用下面的代码来实现WCF的服务端:

  


using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using System.ServiceModel;
  using Service;
  using Contract;
  namespace Host
  {
  class Program
  {
  static void Main(string[] args)
  {
  Uri uri = new Uri("net.tcp://localhost:8080/DataAccessService");
  using (ServiceHost sh = new ServiceHost(typeof(DataAccess), uri))
  {
  NetTcpBinding ctb = new NetTcpBinding();
  sh.AddServiceEndpoint(typeof(IDataAccess), ctb, string.Empty);
  sh.Opened += delegate { Console.WriteLine("服务已经启动"); };
  sh.Open();
  Console.ReadLine();
  }
  }
  }
  }

  在WebSite项目中的App_Code文件夹下创建一个用户调用服务的类,GetService.cs:

 


 using System;
  using System.Data;
  using System.Configuration;
  using System.Linq;
  using System.Web;
  using System.Web.Security;
  using System.Web.UI;
  using System.Web.UI.WebControls;
  using System.Web.UI.WebControls.WebParts;
  using System.Web.UI.HtmlControls;
  using System.Xml.Linq;
  using Contract;
  using System.ServiceModel.Description;
  using System.ServiceModel;
  public class GetService
  {
  public static IDataAccess GetDataAccessService()
  {
  ServiceEndpoint sep = new ServiceEndpoint(ContractDescription.GetContract(typeof(IDataAccess)),
  new NetTcpBinding(),
  new EndpointAddress("net.tcp://localhost:8080/DataAccessService"));
  ChannelFactory cf = new ChannelFactory(sep);
  return cf.CreateChannel();
  }
  }

  调用服务

  最后,就可以调用数据访问服务来进行留言、回复、删除留言等操作了。页面的代码不再贴了,大家可以看第三篇或者下载源代码。我们把Default.cs修改成如下:

  


public partial class _Default : System.Web.UI.Page
  {
  protected void Page_Load(object sender, EventArgs e)
  {
  if (!IsPostBack)
  {
  SetBind();
  }
  }
  protected void btn_SendMessage_Click(object sender, EventArgs e)
  {
  TbGuestBook gb = new TbGuestBook();
  gb.ID = Guid.NewGuid();
  gb.IsReplied = false;
  gb.PostTime = DateTime.Now;
  gb.UserName = tb_UserName.Text;
  gb.Message = tb_Message.Text;
  GetService.GetDataAccessService().SendMessage(gb);
  SetBind();
  }
  private void SetBind()
  {
  rpt_Message.DataSource = GetService.GetDataAccessService().GetData();
  rpt_Message.DataBind();
  }
  }
  Admin.cs代码修改成如下:
  public partial class Admin : System.Web.UI.Page
  {
  protected void Page_Load(object sender, EventArgs e)
  {
  if (!IsPostBack)
  {
  SetBind();
  }
  }
  private void SetBind()
  {
  rpt_Message.DataSource = GetService.GetDataAccessService().GetData();
  rpt_Message.DataBind();
  }
  protected void rpt_Message_ItemCommand(object source, RepeaterCommandEventArgs e)
  {
  if (e.CommandName == "DeleteMessage")
  {
  GetService.GetDataAccessService().DeleteMessage(e.CommandArgument.ToString());
  SetBind();
  }
  if (e.CommandName == "SendReply")
  {
  TbGuestBook gb = new TbGuestBook();
  gb.ID = new Guid(e.CommandArgument.ToString());
  gb.Reply = ((TextBox)e.Item.FindControl("tb_Reply")).Text;
  GetService.GetDataAccessService().SendReply(gb);
  SetBind();
  }
  }
  }

  就这样实现了一个多层构架的留言簿程序。对于WCF的一些内容本文不多作解释了。点击这里下载本篇代码。

  如果您觉得这个例子太简单,还可以在这里下载一个Linq/WCF/MVC结合使用更复杂的例子,此例的目的主要演示一个框架,实现不完整。

  一步一步学Linq to sql到这里就结束了,看到这里应该已经算师父领进门了,后续的提高还要靠大家自己去琢磨。

Linq To Xml学习

Linq To Xml学习 - 1. 概述

2008-05-13 15:46 作者: Q.Lee.lulu 出处: 天极网 责任编辑:>dizzarz

  LINQ to XML 是一种启用了 LINQ 的内存 XML 编程接口,使用它,可以在 .NET Framework 编程语言中处理 XML

  它将 XML 文档置于内存中,这一点很像文档对象模型 (DOM)。 您可以查询和修改 XML 文档,修改之后,可以将其另存为文件,也可以将其序列化然后通过网络发送。 但是,LINQ to XML 与 DOM 不同: 它提供一种新的对象模型,这是一种更轻量的模型,使用也更方便,这种模型利用了 Visual C# 2008 在语言方面的改进。

  LINQ to XML 最重要的优势是它与 Language-Integrated Query (LINQ) 的集成。 由于实现了这一集成,因此,可以对内存 XML 文档编写查询,以检索元素和属性的集合。 LINQ to XML 的查询功能在功能上(尽管不是在语法上)与 XPath 和 XQuery 具有可比性。 Visual C# 2008 集成 LINQ 后,可提供更强的类型化功能、编译时检查和改进的调试器支持。

  通过将查询结果用作 XElement 和 XAttribute 对象构造函数的参数,实现了一种功能强大的创建 XML 树的方法。 这种方法称为“函数构造”,利用这种方法,开发人员可以方便地将 XML 树从一种形状转换为另一种形状。

  LINQ to XML 提供了改进的 XML 编程接口,这一点可能与 LINQ to XML 的 LINQ 功能同样重要。 通过 LINQ to XML,对 XML 编程时,您可以实现任何预期的操作,包括:

  •   从文件或流加载 XML。
  •   将 XML 序列化为文件或流。
  •   使用函数构造从头开始创建 XML。
  •   使用类似 XPath 的轴查询 XML。
  •   使用 Add、Remove、ReplaceWith 和 SetValue 等方法对内存 XML 树进行操作。
  •   使用 XSD 验证 XML 树。
  •   使用这些功能的组合,可将 XML 树从一种形状转换为另一种形状。

  创建 XML 树是否方便,这一点非常重要。 例如,若要创建一个小型 XML 树,可以编写以下 C# 代码:

  


XElement contacts =
  new XElement("Contacts",
  new XElement("Contact",
  new XElement("Name", "Patrick Hines"),
  new XElement("Phone", "206-555-0144",
  new XAttribute("Type", "Home")),
  new XElement("phone", "425-555-0145",
  new XAttribute("Type", "Work")),
  new XElement("Address",
  new XElement("Street1", "123 Main St"),
  new XElement("City", "Mercer Island"),
  new XElement("State", "WA"),
  new XElement("Postal", "68042")
  )
  )
  );

  请注意,缩进用于构造 XML 树的代码可显示基础 XML 的结构。

  直接使用 XML 元素

  在使用 XML 编程时,主要关注的通常是 XML 元素,也可能关注属性。 在 LINQ to XML 中,可以直接使用 XML 元素和属性。 例如,可以执行以下操作:

  •   创建 XML 元素而根本不使用文档对象。 当必须使用 XML 树的片段时,这可简化编程。
  •   直接从 XML 文件加载 T:System.Xml.Linq.XElement 对象。
  •   将 T:System.Xml.Linq.XElement 对象序列化为文件或流。

   使用 LINQ to XML 时,仅在文档的根级别添加注释或处理说明时,才需使用 XDocument 类

  名称和命名空间的简化处理

  处理名称、命名空间和命名空间前缀通常是 XML 编程的复杂部分。LINQ to XML 完全不需要处理命名空间前缀,从而简化了名称和命名空间。 可以轻松控制命名空间前缀。 但如果您决定不显式控制命名空间前缀,则在序列化时,LINQ to XML 将会分配命名空间前缀(如果需要)或使用默认命名空间进行序列化。 如果使用默认命名空间,则生成的文档中将没有命名空间前缀。

  LINQ to XML 也不需要用户理解什么是 NameTable 以及其使用方式。

Linq To Xml学习 - 2 编程概述

2008-05-13 15:57 作者: Q.Lee.lulu 出处: 天极网 责任编辑:>dizzarz

  LINQ to XML 类概述

  XAttribute

  XAttribute 表示一个 XML 属性。

  XCData

  XCData 表示一个 CDATA 文本节点。

  XComment

  XComment 表示一个 XML 注释。

  XContainer

  XContainer 是适用于可能具有子节点的所有节点的抽象基类。 下面的类派生自 XContainer 类:

  XElement

  XDocument

  XDeclaration

  XDeclaration 表示一个 XML 声明。 XML 声明用于声明 XML 版本和文档的编码。 此外,XML 声明还指定 XML 文档是否为独立文档。

  XDocument

  XDocument 表示一个 XML 文档。

  XDocumentType

  XDocumentType 表示一个 XML 文档类型定义 (DTD)。

  XElement

  XElement 表示一个 XML 元素。 有关详细信息和示例,

  XName

  XName 表示元素 (XElement) 和属性 (XAttribute) 的名称。 有关详细信息和示例,

  LINQ to XML 旨在使 XML 名称尽可能简单。 XML 名称由于复杂而通常被视为 XML 中的高级主题。 有证据证明,这种复杂性不是由开发人员编程时通常使用的命名空间造成的,而是由命名空间前缀造成的。 使用命名空间前缀可以减少输入 XML 时需要的击键数或使 XML 更具可读性。 但前缀通常只是使用完整 XML 命名空间的快捷方式,在多数情况下并不需要。LINQ to XML 通过将所有前缀解析为其对应的 XML 命名空间来简化 XML 名称。 如果需要,可以通过 GetPrefixOfNamespace 方法可以使用前缀。

  如果有必要,可以控制命名空间前缀。 在某些情况下,如果使用的是其他 XML 系统(如 XSLT 或 XAML),则需要控制命名空间前缀。 例如,如果 XPath 表达式使用 XSLT 样式表中嵌入的命名空间前缀,则将需要确保使用与 XPath 表达式中使用的前缀相匹配的命名空间前缀来序列化 XML 文档。

  XNamespace 类

  XNamespace 表示 XElement 或 XAttribute 的命名空间。 命名空间是 XName 的一个组件。

  XNode 类

  XNode 是一个抽象类,它表示 XML 树的节点。 下面的类派生自 XNode 类:

  XText

  XContainer

  XComment

  XProcessingInstruction

  XDocumentType

  XNodeDocumentOrderComparer 类

  XNodeDocumentOrderComparer 提供用于比较节点的文档顺序的功能。

  XNodeEqualityComparer 类

  XNodeEqualityComparer 提供用于比较节点的值是否相等的功能。

  XObject 类

  XObject XNode XAttribute 的抽象基类。 它提供批注和事件功能。

  XObjectChange

  XObjectChange 指定对 XObject 引发事件时的事件类型。

  XObjectChangeEventArgs

  XObjectChangeEventArgs 为 Changing 和 Changed 事件提供数据。

  XProcessingInstruction

  XProcessingInstruction 表示一个 XML 处理指令。 处理指令将信息传递给处理 XML 的应用程序。

  XText

  XText 表示一个文本节点。 多数情况下都不必使用此类。 此类主要用于混合内容。

  XDocument 类概述

  XDocument 类包含有效的 XML 文档所需的信息。 其中包括 XML 声明、处理指令和注释。

  请注意,如果需要 XDocument 类提供的特定功能,您只需创建 XDocument 对象。 在很多情况下,可以直接使用 XElement。 直接使用 XElement 是一种比较简单的编程模型。

  XDocument 是从 XContainer 派生的。 因此,它可以包含子节点。 但是,XDocument 对象只能有一个子 XElement 节点。 这反映了 XML 标准,即在 XML 文档中只能有一个根元素。

  在没有 Xdocument 的情况下使用 XElement

  如上所述,XElement 类是 LINQ to XML 编程接口中的主类。 在很多情况下,您的应用程序不需要您创建文档。 通过使用 XElement 类,可以创建 XML 树,向它添加其他 XML 树,修改 XML 树并进行保存。

  若要构造一个 XDocument,可使用函数构造,正如您构造 XElement 对象那样。

  下面的代码创建一个 XDocument 对象及其关联的包含对象。


  XDocument d = new XDocument(
  new XComment("This is a comment."),
  new XProcessingInstruction("xml-stylesheet",
  "href='mystyle.css' title='Compact' type='text/css'"),
  new XElement("Pubs",
  new XElement("Book",
  new XElement("Title", "Artifacts of Roman Civilization"),
  new XElement("Author", "Moreno, Jordao")
  ),
  new XElement("Book",
  new XElement("Title", "Midieval Tools and Implements"),
  new XElement("Author", "Gazit, Inbar")
  )
  ),
  new XComment("This is another comment.")
  );
  d.Declaration = new XDeclaration("1.0", "utf-8", "true");
  Console.WriteLine(d);
  d.Save("test.xml");

  当您检查文件 test.xml 时, 会得到以下输出:

  

 

    Artifacts of Roman Civilization

    Moreno, Jordao

 

 

    Midieval Tools and Implements

    Gazit, Inbar

 

  XElement 类概述

  XElement 类是 LINQ to XML 中的基础类之一。 它表示一个 XML 元素。 可以使用该类创建元素;更改元素内容;添加、更改或删除子元素;向元素中添加属性;或以文本格式序列化元素内容。 还可以与 System.Xml 中的其他类(例如 XmlReader、XmlWriter 和 XslCompiledTransform)进行互操作.

  XElement 类提供的功能。

  构造 XML

  可以使用各种方式构造 XML 树,包括以下方式:

  可以在代码中构造 XML 树。

  可以从包括 TextReader、文本文件或 Web 地址 (URL) 在内的各种源解析 XML。

  可以使用 XmlReader 来填充树。 有关更多信息,请参见 ReadFrom。

  如果您有一个可以将内容写入 XmlWriter 的模块,则可以使用 CreateWriter 方法来创建编写器,将该编写器传递到该模块,然后使用写入 XmlWriter 的内容来填充 XML 树。

  但是,创建 XML 树的最常见的方法如下:


  XElement contacts =
  new XElement("Contacts",
  new XElement("Contact",
  new XElement("Name", "Patrick Hines"),
  new XElement("Phone", "206-555-0144"),
  new XElement("Address",
  new XElement("Street1", "123 Main St"),
  new XElement("City", "Mercer Island"),
  new XElement("State", "WA"),
  new XElement("Postal", "68042")
  )
  )
  );

  另一个创建 XML 树的十分常用的方法是使用 LINQ 查询的结果来填充 XML 树,如下面的示例所示:

  


XElement srcTree = new XElement("Root",
  new XElement("Element", 1),
  new XElement("Element", 2),
  new XElement("Element", 3),
  new XElement("Element", 4),
  new XElement("Element", 5)
  );
  XElement xmlTree = new XElement("Root",
  new XElement("Child", 1),
  new XElement("Child", 2),
  from el in srcTree.Elements()
  where (int)el > 2
  select el
  );
  Console.WriteLine(xmlTree);

  此示例产生以下输出:

 

  1

  2

  3

  4

  5

C# 2008 学习笔记 - LINQ to XML

2008-05-13 16:26 作者: sunrack 出处: 天极网 责任编辑:>dizzarz

  一、命名空间

  System.Xml.Linq.dll 程序集定义了三个命名空间:System.Xml.Linq, System.Xml.Schema 和 System.Xml.XPath

  最核心的是 System.Xml.Linq, 定义了对应 XML 文档个方面的很多类型

  

Member of System.Xml.Linq

Meaning in Life

XAttribute

Represents an XML attribute on a given XML element

XComment

Represents an XML comment

XDeclaration

Represents the opening declaration of an XML document

XDocument

Represents the entirety of an XML document

XElement

Represents a given element within an XML document

XName/XNamespace

Provide a very simple manner to define and reference XML namespaces

     二、编程方式创建XML文档

  以前的 .NET XML编程模型需要使用很多冗长的 DOM API,而 LINQ to XML 则完全可以用与 DOM 无关的方式与 XML 文档交互。这样不但大大减少了代码行,而且这种编程模型可以直接映射到格式良好的XML文档结构。

 


 static void CreateFunctionalXmlElement()
  {
  // A "functional" approach to build an
  // XML element in memory.
  XElement inventory =
  new XElement("Inventory",
  new XElement("Car", new XAttribute("ID", "1"),
  new XElement("Color", "Green"),
  new XElement("Make", "BMW"),
  new XElement("PetName", "Stan")
  )
  );
  // Call ToString() on our XElement.
  Console.WriteLine(inventory);
  }

  在内存中创建XML文档

  


static void CreateFunctionalXmlDoc()
  {
  XDocument inventoryDoc =
  new XDocument(
  new XDeclaration("1.0", "utf-8", "yes"),
  new XComment("Current Inventory of AutoLot"),
  new XElement("Inventory",
  new XElement("Car", new XAttribute("ID", "1"),
  new XElement("Color", "Green"),
  new XElement("Make", "BMW"),
  new XElement("PetName", "Stan")
  ),
  new XElement("Car", new XAttribute("ID", "2"),
  new XElement("Color", "Pink"),
  new XElement("Make", "Yugo"),
  new XElement("PetName", "Melvin")
  )
  )
  );
  // Display the document and save to disk.
  Console.WriteLine(inventoryDoc);
  inventoryDoc.Save("SimpleInventory.xml");
  }

  三、使用 LINQ 查询创建XML文档

 


 static void CreateXmlDocFromArray()
  {
  // Create an anonymous array of anonymous types.
  var data = new [] {
  new { PetName = "Melvin", ID = 10 },
  new { PetName = "Pat", ID = 11 },
  new { PetName = "Danny", ID = 12 },
  new { PetName = "Clunker", ID = 13 }
  };
  // Now enumerate over the array to build
  // an XElement.
  XElement vehicles =
  new XElement("Inventory",
  from c in data
  select new XElement("Car",
  new XAttribute("ID", c.ID),
  new XElement("PetName", c.PetName)
  )
  );
  Console.WriteLine(vehicles);
  }

  四、加载和解析XML内容

  


static void LoadExistingXml()
  {
  // Build an XElement from string.
  string myElement =
  @"
  Yellow
  Yugo
  ";
  XElement newElement = XElement.Parse(myElement);
  Console.WriteLine(newElement);
  Console.WriteLine();
  // Load the SimpleInventory.xml file.
  XDocument myDoc = XDocument.Load("SimpleInventory.xml");
  Console.WriteLine(myDoc);
  }

  六、遍历内存中的XML文档

  XML示例:

 



  
    Ford
    Blue
    Chuck
  
  
    VW
    Silver
    Mary
  
  
    Yugo
    Pink
    Gipper
  
  
    Ford
    Yellow
    862 CHAPTER 24 n PROGRAMMING WITH THE LINQ APIS
    Max
  
  
    BMW
    Black
    Zippy
  

  加载

  


static void Main(string[] args)
  {
  Console.WriteLine("***** Fun with LINQ to XML *****\n");
  // Load the Inventory.xml document into memory.
  XElement doc = XElement.Load("Inventory.xml");
  // We will author each of these next
  PrintAllPetNames(doc);
  Console.WriteLine();
  GetAllFords(doc);
  Console.ReadLine();
  }

  遍历


  static void PrintAllPetNames(XElement doc)
  {
  var petNames = from pn in doc.Descendants("PetName")
  select pn.Value;
  foreach (var name in petNames)
  Console.WriteLine("Name: {0}", name);
  }

  查询

 


 static void GetAllFords(XElement doc)
  {
  var fords = from c in doc.Descendants("Make")
  where c.Value == "Ford"
  select c;
  foreach (var f in fords)
  Console.WriteLine("Name: {0}", f);
  }

  七、修改 XML文档

 


 static void AddNewElements(XElement doc)
  {
  // Add 5 new purple Fords to the incoming document.
  for (int i = 0; i < 5; i++)
  {
  // Create a new XElement
  XElement newCar =
  new XElement("Car", new XAttribute("ID", i + 1000),
  new XElement("Color", "Green"),
  new XElement("Make", "Ford"),
  new XElement("PetName", "")
  );
  // Add to doc.
  doc.Add(newCar);
  }
  // Show the updates.
  Console.WriteLine(doc);
  }

使用linq to xml 快速创建自己的Rss 

2008-05-26 15:24 作者: 半途的Blog 出处: 天极网 责任编辑:>dizzarz

  首先引用网络上不知道是谁的一段文字来讲述下什么是linq to xml 如果已经熟悉的就直接跳过这段:

  LINQ to XML 是一种启用了 LINQ 内存 XML 编程接口,使用它,可以在 .NET Framework 编程语言中处理 XML

  它将 XML 文档置于内存中,这一点很像文档对象模型 (DOM)。 您可以查询和修改 XML 文档,修改之后,可以将其另存为文件,也可以将其序列化然后通过网络发送。 但是,LINQ to XML 与 DOM 不同: 它提供一种新的对象模型,这是一种更轻量的模型,使用也更方便,这种模型利用了 Visual C# 2008 在语言方面的改进。

  LINQ to XML 最重要的优势是它与 Language-Integrated Query (LINQ) 的集成。 由于实现了这一集成,因此,可以对内存 XML 文档编写查询,以检索元素和属性的集合。 LINQ to XML 的查询功能在功能上(尽管不是在语法上)与 XPath 和 XQuery 具有可比性。 Visual C# 2008 集成 LINQ 后,可提供更强的类型化功能、编译时检查和改进的调试器支持。

  通过将查询结果用作 XElement 和 XAttribute 对象构造函数的参数,实现了一种功能强大的创建 XML 树的方法。 这种方法称为“函数构造”,利用这种方法,开发人员可以方便地将 XML 树从一种形状转换为另一种形状。

  LINQ to XML 提供了改进的 XML 编程接口,这一点可能与 LINQ to XML 的 LINQ 功能同样重要。 通过 LINQ to XML,对 XML 编程时,您可以实现任何预期的操作,包括:

  ·   从文件或流加载 XML。

  ·   将 XML 序列化为文件或流。

  ·   使用函数构造从头开始创建 XML。

  ·   使用类似 XPath 的轴查询 XML。

  ·   使用 Add、Remove、ReplaceWith 和 SetValue 等方法对内存 XML 树进行操作。

  ·   使用 XSD 验证 XML 树。

  ·   使用这些功能的组合,可将 XML 树从一种形状转换为另一种形状。

  好了,我开始废话,首先说一下呢,我技术平平,写出来的文章自然也是不怎么高水平的,但是都是我自己实际项目中总结出来的。

  错误之处还请大家指正,转载的朋友请注明出处:『半途』

  现在言归正传说我们的正题

  首先我们创建一个新窗体,这里以我的网站为例,我们起名叫Rss.aspx

  创建完成后,如果Rss数据源是XML实际文档,就跳过下面一步,如果Rss数据源是aspx文件

  请在创建完新页面后切换到源视图

  保留这一行:


<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Rss.aspx.cs" Inherits="Rss" %> 

  其余的html代码全部删除

  然后在cs里创建方法createRss()

  具体看代码和注释:

 


 public void createRss()
  {
  //首先从数据库里读出所需要的数据
  XXXXContext bd = new XXXXContext();
  var sq = from p in bd.news orderby p.ID descending select new { p.ID, p.title,p.cont, p.in_date,p.n_type };
  //开始创建xml 对于Rss的规范我这里不再啰嗦了,不明白的可以去网上查一下
  //下面这一部分为xml里的声明部分,从foreach开始循环item,把sq的数据循环写入xml
  XElement contacts = new XElement("rss", new XAttribute("version", "2.0"),
  new XElement("channel",
  new XElement("title", "半途的Rss"),
  new XElement("link", "http://www.bantool.cn"),
  new XElement("description", "C#,linq,vs2008"),
  new XElement("language", "zh_cn"),
  new XElement("copyright", "Copyright 2008 bantool"),
  new XElement("webMaster", "[email protected]")));
  //这里开始循环写数据
  foreach (var p in sq)
  {
  contacts.Element("channel").Add(new XElement("item",
  new XElement("title", p.title),
  new XElement("link", "http://www.bantool.cn/news" + p.ID + ".bantool"),
  new XElement("description", p.cont),
  new XElement("category", p.n_type),
  new XElement("pubDate", p.in_date)));
  }
  //这里开始操作xml是写入具体的xml文件还是直接输出
  //如果是写入文件的话用这个
  //contacts.Save("文件路径");
  //如果是输出的諙,
  //先设好输出的类型"text/xml"
  Response.ContentType = "text/xml";
  using (XmlWriter writer = new XmlTextWriter(Response.OutputStream, Encoding.UTF8))
  //用contacts的WriteTo方法来写
  contacts.WriteTo(writer);
  //这样就OK啦
  }

  好了,这个样子就完成了,看我们的linq多么强大!呵呵

转载于:https://www.cnblogs.com/freedom831215/archive/2011/03/22/1991143.html

你可能感兴趣的:(数据库,c#,人工智能)