
LINQ To SQL 增、删、改
添加 InsertOnSubmit(单个对象) 或 InsertAllOnSubmit(集合)
删除 DeleteOnSubmit (单个对象) DeleteAllOnSubmit(集合)
SubmitChanges() 提交数据库
//创建LINQDB数据库上下文的实例
添加
LinqDBDataContext db = new LinqDBDataContext(LinqSystem.LinqDBConnectionString);
//创建用户对象
UserInfo user = new UserInfo();
user.Username = t_UserName.Text; //赋值
user.Password = t_PassWord.Text;
user.Email = t_Email.Text;
try
{
db.UserInfo.InsertOnSubmit(user);//添加到数据库中
db.SubmitChanges();//提交更新
Response.Write(“”);
}
catch (Exception)
{
throw;
}
更新
LinqDBDataContext db = new LinqDBDataContext(LinqSystem.LinqDBConnectionString);
var result = from u in db.UserInfo
where u.ID == Convert.ToInt32(e.CommandArgument)
select u; //找到需要修改记录
foreach (var item in result) //重新赋值
{
item.Username = t_UserName.Text;
item.Email = t_Email.Text;
}
db.SubmitChanges();//提交修改
删除
LinqDBDataContext db = new LinqDBDataContext(LinqSystem.LinqDBConnectionString);
int id=Convert.ToInt32(e.CommandArgument);
var result = from UserInfo u in db.UserInfo
where u.ID == id
select u; //找到需要删除的记录
db.UserInfo.DeleteAllOnSubmit(result);
db.SubmitChanges();
1、Syntax Query
var xx=from i in x select i
2、Syntax Method
var db1=db.table.where(r=>r.age>10)
1、本地方法调用形式(LocalMethodCall)
var q = from c in db.Customers
where c.Country == “UK” || c.Country == “USA”
select new
{
c.CustomerID,
c.CompanyName,
Phone = c.Phone,
InternationalPhone = PhoneNumberConverter(c.Country, c.Phone)
};
public string PhoneNumberConverter(string Country, string Phone)
{
Phone = Phone.Replace(" “, “”).Replace(”)", “)-”);
switch (Country)
{
case “USA”:
return “1-” + Phone;
case “UK”:
return “44-” + Phone;
default:
return Phone;
}
}
2、指定类型形式: 使用SELECT和已知类型返回雇员姓名的序列
var q =
from e in db.Employees
select new Name
{
FirstName = e.FirstName,
LastName = e.LastName
};
3、条件形式:生成SQL语句为:case when condition then else
var q =
from p in db.Products
select new
{
p.ProductName,
Availability =
p.UnitsInStock - p.UnitsOnOrder < 0 ? “Out Of Stock” : “In Stock”
};
4、shaped形式(异型类型) 其select操作使用了匿名对象,而这个匿名对象中,其属性也是个匿名对象
var q =
from c in db.Customers
select new {
c.CustomerID,
CompanyInfo = new {
c.CompanyName,
c.City, c.Country
},
ContactInfo = new {
c.ContactName,
c.ContactTitle
}
};
5、嵌套类型形式 返回的对象集中的每个对象DiscountedProducts属性中,又包含一个集合。也就是每个对象也是一个集合类。
var q =
from o in db.Orders
select new {
o.OrderID,
DiscountedProducts =
from od in o.OrderDetails
where od.Discount > 0.0
select od,
FreeShippingDiscount = o.Freight
};
6、Distinct形式:筛选字段中不相同的值。用于查询不重复的结果集。生成SQL语句为:SELECT DISTINCT [City] FROM [Customers]
这个是立即执行的,以上都是延后加载的,
var q = (
from c in db.Customers
select c.City )
.Distinct();

Count/Sum/Min/Max/Avg
Count:返回集合中的元素个数,返回INT类型;不延迟。生成SQL语句为:SELECT COUNT(*) FROM
var q = db.Customers.Count();//1.简单形式:
var q = db.Products.Count(p => !p.Discontinued);//2.带条件形式:
Sum:返回集合中数值类型元素之和,集合应为INT类型集合;不延迟。生成SQL语句为:SELECT SUM(…) FROM
var q = db.Orders.Select(o => o.Freight).Sum();//1.简单形式
var q = db.Products.Sum(p => p.UnitsOnOrder);//2.映射形式:



Exists/In/Any/All/Contains
Any
说明:用于判断集合中是否有元素满足某一条件;不延迟。(若条件为空,则集合只要不为空就返回True,否则为False)。有2种形式,分别为简单形式
和带条件形式

2.带条件形式:
仅返回至少有一种产品断货的类别:
var q =
from c in db.Categories
where c.Products.Any(p => p.Discontinued)
select c;
生成SQL语句为:
SELECT [t0].[CategoryID], [t0].[CategoryName], [t0].[Description],
[t0].[Picture] FROM [dbo].[Categories] AS [t0]
WHERE EXISTS(
SELECT NULL AS [EMPTY] FROM [dbo].[Products] AS [t1]
WHERE ([t1].[Discontinued] = 1) AND
([t1].[CategoryID] = [t0].[CategoryID])
)
All
说明:用于判断集合中所有元素是否都满足某一条件;不延迟
var q =
from c in db.Customers
where c.Orders.All(o => o.ShipCity == c.City)
select c;
Contains
说明:用于判断集合中是否包含有某一元素;不延迟。它是对两个序列进行连接操作的。
string[] customerID_Set =
new string[] { “AROUT”, “BOLID”, “FISSA” };
var q = (
from o in db.Orders
where customerID_Set.Contains(o.CustomerID)
select o).ToList();
var q = (
from o in db.Orders
where (
new string[] { “AROUT”, “BOLID”, “FISSA” })
.Contains(o.CustomerID)
select o).ToList();
Not Contains则取反:
var q = (
from o in db.Orders
where !(
new string[] { “AROUT”, “BOLID”, “FISSA” })
.Contains(o.CustomerID)
select o).ToList();
1.包含一个对象:
var order = (from o in db.Orders
where o.OrderID == 10248
select o).First();
var q = db.Customers.Where(p => p.Orders.Contains(order)).ToList();
foreach (var cust in q)
{
foreach (var ord in cust.Orders)
{
//do something
}
}
2.包含多个值:
string[] cities = new string[] { “Seattle”, “London”, “Vancouver”, “Paris” };
var q = db.Customers.Where(p=>cities.Contains(p.City)).ToList();
Concat/Union/Intersect/Except
适用场景:对两个集合的处理,例如追加、合并、取相同项、相交项等等。
Concat(连接)
说明:连接不同的集合,不会自动过滤相同项;延迟。

Union(合并)
说明:连接不同的集合,自动过滤相同项;延迟。即是将两个集合进行合并操作,过滤相同的项。 SQL Union
语句描述:查询顾客和职员所在的国家。
var q = (
from c in db.Customers
select c.Country
).Union(
from e in db.Employees
select e.Country
);

之Top/Bottom和Paging和SqlMethods
适用场景:适量的取出自己想要的数据,不是全部取出,这样性能有所加强

SkipWhile说明:直到某一条件成立就停止跳过;延迟。即用其条件去判断源序列中的元素并且跳过第一个符合判断条件的元素,一旦判断返回false,接下来将不再
进行判断并返回剩下的所有元素。


//3.查询城市为Seattle的消费者
var SeaCusts = fn(db, “Seattle”);
语句描述:这个例子创建一个已编译查询,然后使用它检索输入城市的客户。
Insert

3.多对多关系
说明:在多对多关系中,我们需要依次提交。
var newEmployee = new Employee
{
FirstName = “Kira”,
LastName = “Smith”
};
var newTerritory = new Territory
{
TerritoryID = “12345”,
TerritoryDescription = “Anytown”,
Region = db.Regions.First()
};
var newEmployeeTerritory = new EmployeeTerritory
{
Employee = newEmployee,
Territory = newTerritory
};
db.Employees.InsertOnSubmit(newEmployee);
db.Territories.InsertOnSubmit(newTerritory);
db.EmployeeTerritories.InsertOnSubmit(newEmployeeTerritory);
db.SubmitChanges();
语句描述:使用InsertOnSubmit方法将新雇员添加到Employees 表中,将新Territory添加到Territories表中,并将新EmployeeTerritory对象添加到与此新Employee对象和新Territory对象有外键关系的EmployeeTerritories表中。调用SubmitChanges将这些新对象及其关系保持到数据库。
之Update
说明:更新操作,先获取对象,进行修改操作之后,直接调用SubmitChanges()方法即可提交。注意,这里是在同一个DataContext中,对于不同的DataContex看下面的讲解。

Delete和使用Attach
1.简单形式
说明:调用DeleteOnSubmit方法即可。
OrderDetail orderDetail = db.OrderDetails.First(c => c.OrderID == 10255 && c.ProductID == 36);
db.OrderDetails.DeleteOnSubmit(orderDetail);
db.SubmitChanges();
语句描述:使用DeleteOnSubmit方法从OrderDetail 表中删除OrderDetail对象。调用SubmitChanges 将此删除保持到数据库。
2.一对多关系
说明:Order与OrderDetail是一对多关系,首先DeleteOnSubmit其OrderDetail(多端),其次DeleteOnSubmit其Order(一端)。因为一端是主键。
var orderDetails =
from o in db.OrderDetails
where o.Order.CustomerID == “WARTH” &&
o.Order.EmployeeID == 3
select o;
var order =
(from o in db.Orders
where o.CustomerID == “WARTH” && o.EmployeeID == 3
select o).First();
foreach (OrderDetail od in orderDetails)
{
db.OrderDetails.DeleteOnSubmit(od);
}
db.Orders.DeleteOnSubmit(order);
db.SubmitChanges();
语句描述语句描述:使用DeleteOnSubmit方法从Order 和Order Details表中删除Order和Order Detail对象。首先从Order Details删除,然后从Orders删除。调用SubmitChanges将此删除保持到数据库。

使用Attach更新(Update with Attach)
说明:在对于在不同的DataContext之间,使用Attach方法来更新数据。例如在一个名为tempdb的NorthwindDataContext中,查询出Customer和Order,
在另一个NorthwindDataContext中,Customer的地址更新为123 First Ave,Order的CustomerID 更新为CHOPS。
//通常,通过从其他层反序列化 XML 来获取要附加的实体
//不支持将实体从一个DataContext附加到另一个DataContext
//因此若要复制反序列化实体的操作,将在此处重新创建这些实体
Customer c1;
List deserializedOrders = new List();
Customer deserializedC1;
using (NorthwindDataContext tempdb = new NorthwindDataContext())
{
c1 = tempdb.Customers.Single(c => c.CustomerID == “ALFKI”);
deserializedC1 = new Customer
{
Address = c1.Address,
City = c1.City,
CompanyName = c1.CompanyName,
ContactName = c1.ContactName,
ContactTitle = c1.ContactTitle,
Country = c1.Country,
CustomerID = c1.CustomerID,
Fax = c1.Fax,
Phone = c1.Phone,
PostalCode = c1.PostalCode,
Region = c1.Region
};
Customer tempcust =
tempdb.Customers.Single(c => c.CustomerID == “ANTON”);
foreach (Order o in tempcust.Orders)
{
deserializedOrders.Add(new Order
{
CustomerID = o.CustomerID,
EmployeeID = o.EmployeeID,
Freight = o.Freight,
OrderDate = o.OrderDate,
OrderID = o.OrderID,
RequiredDate = o.RequiredDate,
ShipAddress = o.ShipAddress,
ShipCity = o.ShipCity,
ShipName = o.ShipName,
ShipCountry = o.ShipCountry,
ShippedDate = o.ShippedDate,
ShipPostalCode = o.ShipPostalCode,
ShipRegion = o.ShipRegion,
ShipVia = o.ShipVia
});
}
}
using (NorthwindDataContext db2 = new NorthwindDataContext())
{
//将第一个实体附加到当前数据上下文,以跟踪更改
//对Customer更新,不能写错
db2.Customers.Attach(deserializedC1);
//更改所跟踪的实体
deserializedC1.Address = “123 First Ave”;
//附加订单列表中的所有实体
db2.Orders.AttachAll(deserializedOrders);
//将订单更新为属于其他客户
foreach (Order o in deserializedOrders)
{
o.CustomerID = “CHOPS”;
}
//在当前数据上下文中提交更改
db2.SubmitChanges();
}
语句描述:从另一个层中获取实体,使用Attach和AttachAll将反序列化后的实体附加到数据上下文,然后更新实体。更改被提交到数据库。
使用Attach更新和删除(Update and Delete with Attach)
说明:在不同的DataContext中,实现插入、更新、删除。看下面的一个例子:
//通常,通过从其他层反序列化XML获取要附加的实体
//此示例使用 LoadWith 在一个查询中预先加载客户和订单,
//并禁用延迟加载
Customer cust = null;
using (NorthwindDataContext tempdb = new NorthwindDataContext())
{
DataLoadOptions shape = new DataLoadOptions();
shape.LoadWith(c => c.Orders);
//加载第一个客户实体及其订单
tempdb.LoadOptions = shape;
tempdb.DeferredLoadingEnabled = false;
cust = tempdb.Customers.First(x => x.CustomerID == “ALFKI”);
}
Order orderA = cust.Orders.First();
Order orderB = cust.Orders.First(x => x.OrderID > orderA.OrderID);
using (NorthwindDataContext db2 = new NorthwindDataContext())
{
//将第一个实体附加到当前数据上下文,以跟踪更改
db2.Customers.Attach(cust);
//附加相关订单以进行跟踪; 否则将在提交时插入它们
db2.Orders.AttachAll(cust.Orders.ToList());
//更新客户的Phone.
cust.Phone = “2345 5436”;
//更新第一个订单OrderA的ShipCity.
orderA.ShipCity = “Redmond”;
//移除第二个订单OrderB.
cust.Orders.Remove(orderB);
//添加一个新的订单Order到客户Customer中.
Order orderC = new Order() { ShipCity = “New York” };
cust.Orders.Add(orderC);
//提交执行
db2.SubmitChanges();
}
语句描述:从一个上下文提取实体,并使用 Attach 和 AttachAll 附加来自其他上下文的实体,然后更新这两个实体,删除一个实体,添加另一个实体。更改被提交到数据库
一般都是可视化创建EDM模型的,

二、手动创建 EDM 手动建立实体类
在项目中添加一个类GuestInfoEntity.cs,如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq.Mapping;
namespace DataContexDemo
{
///
/// 手动建立实体类
///
[Table(Name=“tb_GuestInfo”)]
class GuestInfoEntity {
[Column(IsPrimaryKey=true,DbType=“Int NOT NULL IDENTITY”,IsDbGenerated=true,Name=“Id”)]
public int ID { get; set; }
[Column(DbType = “nvarchar(20)”, Name = “Name”)]
public string Name{get;set;}
[Column(DbType = “int”, Name = “Age”)]
public int Age { get; set; }
[Column(DbType = “nvarchar(20)”, Name = “Tel”)]
public string Tel { get; set; }
}
}
编写示例代码,注意需要引入System.Data.Linq.dll:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq;/
namespace DataContexDemo
{
class Program {
static void Main(string[] args) {
//2.手动建立实体类
//
//连接字符串
string constring = @“Data Source=.\SQLEXPRESS;AttachDbFilename=E:\Visual Studio 2010\LINQ_to_SQL\LINQ_To_SQL自定义
nce=True”;
DataContext dc = new DataContext(constring); Table tb = dc.GetTable(); var query = tb.AsEnumerable();
foreach (var q in query) {
Console.WriteLine(“{0} {1} {2} {3}”,q.ID,q.Name,q.Age,q.Tel );
}
Console.ReadKey();
}
}
}
3、使用XML映射文件建立实体类
实体类的映射除了使用内联Attribute外,还可以建立一个包含映射信息的XML文件,此文件生成System.Data.Linq.Mapping.XmlMappingSource对象,作为DataContext
这个XML文件只有一个根节点—Database元素,用来映射的数据库信息。Database元素包含一个或多个Table元素,用于映射数据库表的信息,Table元素由一个Type mn元素用来指定列信息,Association元素用来映射数据库关系。 在项目中添加一个XML文件,采用默认名称XMLFile1.xml,内容如下:
这个XML文件包含类全部的映射信息,下面建立映射的类GuestInfoEntity.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LINQtoSQL建立实体类_XML
{
public class GuestInfoEntity {
public int ID { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Tel { get; set; }
}
}
编写示例代码,同样需要引入System.Data.Linq.dll:
using System;
using System.Collections.Generic;
using System.Linq; using System.Text;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.IO;//
namespace LINQtoSQL建立实体类_XML {
class Program {
static void Main(string[] args) {
string constring = @“Data Source=.\SQLEXPRESS;AttachDbFilename=E:\Visual Studio 2010\LINQ_to_SQL\LINQ_To_SQL自定义 nce=True”;
XmlMappingSource map = XmlMappingSource.FromXml(File.ReadAllText(“XMLFile1.xml”)); DataContext dc = new DataContext(constring, map); Table tb = dc.GetTable(); var query = tb.AsEnumerable();
foreach (var g in query) {
Console.WriteLine(“{0} {1} {2} {3}”,g.ID,g.Name,g.Age,g.Tel );
}
Console.ReadKey();
}
}
}

说明:我们读取数据之后,另外一个用户获取并提交更新了这个数据,这时,我们更新这个数据时,引起了一个并发冲突。系统发生回滚,允许你可以从数据库检索新更新的数据,并决定如何继续进行您自己的更新。
//当前用户
var product = db.Products.First(p => p.ProductID == 1);
//我们打开一个新的连接来模拟另外一个用户
NorthwindDataContext otherUser_db = new NorthwindDataContext() ;
var otherUser_product = otherUser_db.Products.First(p => p.ProductID == 1);
otherUser_product.UnitPrice = 999.99M;
otherUser_db.SubmitChanges();
//当前用户修改 相当于SQl 脏读了
product.UnitPrice = 777.77M;
try
{
db.SubmitChanges();
}
catch (ChangeConflictException)
{
//发生异常!
}
Transactions事务
LINQ to SQL 支持三种事务模型,分别是:
显式本地事务:调用 SubmitChanges 时,如果 Transaction 属性设置为事务,则在同一事务的上下文中执行 SubmitChanges 调用。成功执行事务后,要由您来提交或回滚事务。与事务对应的连接必须与用于构造 DataContext 的连接匹配。如果使用其他连接,则会引发异常。
显式可分发事务:可以在当前 Transaction 的作用域中调用 LINQ to SQL API(包括但不限于 SubmitChanges)。LINQ to SQL 检测到调用是在事务的作用域内,因而不会创建新的事务。在这种情况下,vbtecdlinq 还会避免关闭连接。您可以在此类事务的上下文中执行查询和SubmitChanges 操作。
隐式事务:当您调用 SubmitChanges 时,LINQ to SQL 会检查此调用是否在 Transaction 的作用域内或者 Transaction 属性是否设置为由用户启动的本地事务。如果这两个事务它均未找到,则 LINQ to SQL 启动本地事务,并使用此事务执行所生成的 SQL 命令。当所有 SQL 命令均已成功执行完毕时,LINQ to SQL 提交本地事务并返回。




5.String.StartsWith(prefix)
var q =
from c in db.Customers
where c.ContactName.StartsWith(“Maria”)
select c;
语句描述:这个例子使用StartsWith方法查找联系人姓名以“Maria”开头的客户。



对象加载






运算符转换



之ADO.NET与LINQ to SQL
它基于由 ADO.NET 提供程序模型提供的服务。因此,我们可以将 LINQ to SQL 代码与现有的 ADO.NET 应用程序混合在一起,将当前 ADO.NET 解决 方案迁移到 LINQ to SQL





首先看SelectMany的定义:
Queryable中的SelectMany 方法:将序列的每个元素投影到一个 IEnumerable<(Of <(T>)>) 并将结果序列组合为一个 IQueryable<(Of <(T>)>) 类型的序列。(引用MSDN)
在用LINQ TO SQL 来写查询语句时,有一个selectMany的语句,它标示着一对多的关系,这篇文章我想说下在LINQ TO SQL中几种可以等同selectMany的用法。
系统转换成selectMany的条件:
1:语句中不包含join ,into;
2:需要2个以上的from:下面以两个表为例:如第一个表from c in 表1
1):如果from的对象均用表名,(from c in 表2),则会转换成cross join;
2):如果第二个表名以第一个表的子表形式出现,即类似c.表2,这又分两种情况,
1>:from o in c.表2,此时会形成inner join
2>:from p in c.表2.DefaultIfEmpty(),此时会形成LEFT OUT JOIN
文中例子表结构说明:Customer表和Purchase表,通过ID与CustomerID建立关联。
复制代码
CREATE TABLE [dbo].[Customer](
[ID] [int] NOT NULL,
[Name] nvarchar )
CREATE TABLE [dbo].[Purchase](
[ID] [int] NOT NULL,
[CustomerID] [int] NULL,
[Date] [datetime] NOT NULL,
[Description] varchar )
复制代码
我们来实现SQL中的三种非常经典的联接方式。
第一:cross join,它的结果集是所有表的迪卡尔积。
//cross join
from c in Customers
from o in Purchases
select o
在LINQ TO SQL中,下面的from都指定为表名的话,就会生成下面的语句:
SELECT [t1].[ID], [t1].[CustomerID], [t1].[Date], [t1].[Description], [t1].[Price]
FROM [Customer] AS [t0], [Purchase] AS [t1]
第二:inner join。
//inner join
from c in Customers
from o in c.Purchases
select o
生成的SQL如下:
SELECT [t1].[ID], [t1].[CustomerID], [t1].[Date], [t1].[Description], [t1].[Price]
FROM [Customer] AS [t0], [Purchase] AS [t1]
WHERE [t1].[CustomerID] = [t0].[ID]
虽然没有显示的用inner join,但和它的功能是一样的.它的写法和上面的cross join看起来特别像,唯一的区别就在于cross join时,直接用了表名Purchases,而inner join用的时候变成了c.Pruchasex,即形成了一对多的情况。
第三: LEFT OUTER JOIN
from c in Customers
from p in c.Purchases.DefaultIfEmpty()
select new { c.Name, p.Description, Price = (decimal?) p.Price }
生成的SQL如下:
SELECT [t0].[Name], [t1].[Description] AS [Description], [t1].[Price] AS [Price]
FROM [Customer] AS [t0]
LEFT OUTER JOIN [Purchase] AS [t1] ON [t1].[CustomerID] = [t0].[ID]
left outer join实际上是在inner join的基础上加了一个条件,利用DefaultIfEmpty(),当记录不匹配时,返回null
我们对上在的查询增加一个过滤条件。
复制代码
from c in Customers
from p in c.Purchases.Where (p => p.Price > 1000).DefaultIfEmpty()
select new
{
c.Name,
p.Description,
Price = (decimal?) p.Price
}
复制代码
对应的SQL:
SELECT [t0].[Name], [t1].[Description] AS [Description], [t1].[Price] AS [Price]
FROM [Customer] AS [t0]
LEFT OUTER JOIN [Purchase] AS [t1] ON ([t1].[Price] > @p0) AND ([t1].[CustomerID] = [t0].[ID])
此时上面的语句还是标准的LEFT OUT JOIN,如果我们改变下条件的位置呢?
复制代码
from c in Customers
from p in c.Purchases.DefaultIfEmpty()
where p.Price>1000
select new
{
c.Name,
p.Description,
Price = (decimal?) p.Price
}
复制代码
对应的SQL:
SELECT [t0].[Name], [t1].[Description] AS [Description], [t1].[Price] AS [Price]
FROM [Customer] AS [t0]
LEFT OUTER JOIN [Purchase] AS [t1] ON [t1].[CustomerID] = [t0].[ID]
WHERE [t1].[Price] > @p0
条件改变位置后并没有改变join的本质,还是LEFT OUT JOIN,只不过查询的结果不一样了,从结果集上看,后面一种的效果和inner join的结果一样。
总结:上面的查询语句也可以用显示的join来查询,个人更喜欢用显示的join,因为相比较SQL更接近些,看起来要亲近些。在下篇文章中,我会总结显示用join查询的用法,其实最终的显示结果都一样,只是写法不同。