兴国安邦

C# 3.0, Linq, Linq To Sql

博客园 首页 新随笔 联系 订阅 管理
  33 Posts :: 0 Stories :: 553 Comments :: 150 Trackbacks

CUD就是Create, Update, Delete。在别人都写过了后,还有什么是新鲜的呢?

1,CreateDatabase
            Northwind db = new Northwind("You connection string");  //注意database项,起一个不存在的数据库名称
            db.Log = Console.Out;
            if (!db.DatabaseExists())  //如果,数据库不存在
                db.CreateDatabase();   //创建数据库
这个的好处,就是你可以用OR designer设计实体类,定义其在数据库的各个column,然后,将其返回到数据库。前段时间,和别人争论起,在程序设计时,是先有实体类还是先有实体表时,其主张,是由高层到底层,即先设计实体类,再做表。那这个恰好满足了这个需要。但是,在OR designer上设计实体类的数据库属性时,及其难用,我宁愿根据实体类,去设计数据库中的表,然后,在重新生成这些实体类。

2, Inser记录
2.0
这个操作相当简单。new出来一个对象,使用Add方法将其加入到其对应Entity集合中后,使用SubmitChanges函数即可。
    var newCustomer = new Customer { CustomerID = "MCSFT",
                                     CompanyName = "Microsoft",
                                     ContactName = "John Doe",
                                     ContactTitle = "Sales Manager",
                                     Address = "1 Microsoft Way",
                                     City = "Redmond",
                                     Region = "WA",
                                     PostalCode = "98052",
                                     Country = "USA",
                                     Phone = "(425) 555-1234",
                                     Fax = null
                                   };
    db.Customers.Add(newCustomer);
    db.SubmitChanges();

2.1
如果,数据表中有数据库自动赋值的column的呢?就拿Orders表来说事。其OrderID就是自增型的。看看该字段的映射。

[Column(Storage="_OrderID", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
    
public int OrderID
    
{
        
get
        
{
            
return this._OrderID;
        }

        
set
        
{
            
if ((this._OrderID != value))
            
{
                
this.OnOrderIDChanging(value);
                
this.SendPropertyChanging();
                
this._OrderID = value;
                
this.SendPropertyChanged("OrderID");
                
this.OnOrderIDChanged();
            }

        }

    }

在其Attribute中,有AutoSync=AutoSync.OnInsert. 当有IsDbGenerated为true时,OnInsert为AutoSync默认值。该字段告诉run-time,在插入数据库后,自动更新数据库产生的值。 我们随便来做个测试,看看Linq To Sql做了什么。

            Orders o = new Orders();
            o.ShipAddress 
= "Test";
            db.Orders.Add(o);
            db.SubmitChanges();

            Console.WriteLine(o.OrderID);

你可以扑获如下的sql

INSERT INTO [dbo].[Orders]([CustomerID][EmployeeID][OrderDate][RequiredDate][ShippedDate][ShipVia][Freight][ShipName][ShipAddress][ShipCity][ShipRegion][ShipPostalCode][ShipCountry]VALUES (@p0@p1@p2@p3@p4@p5@p6@p7@p8@p9@p10@p11@p12)

SELECT [t0].[OrderID]
FROM [dbo].[Orders] AS [t0]
WHERE [t0].[OrderID] = (SCOPE_IDENTITY())

-- @p0: Input StringFixedLength (Size = 5; Prec = 0; Scale = 0) []
--
 @p1: Input Int32 (Size = 0; Prec = 0; Scale = 0) []
--
 @p2: Input DateTime (Size = 0; Prec = 0; Scale = 0) []
--
 @p3: Input DateTime (Size = 0; Prec = 0; Scale = 0) []
--
 @p4: Input DateTime (Size = 0; Prec = 0; Scale = 0) []
--
 @p5: Input Int32 (Size = 0; Prec = 0; Scale = 0) []
--
 @p6: Input Currency (Size = 0; Prec = 19; Scale = 4) []
--
 @p7: Input String (Size = 0; Prec = 0; Scale = 0) []
--
 @p8: Input String (Size = 4; Prec = 0; Scale = 0) [Test]
--
 @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) []
--
 @p12: Input String (Size = 0; Prec = 0; Scale = 0) []
--
 Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1

这表明Linq To Sql自动更新了该对象,把数据库自增字段的值取出,赋于该对象。从这里,也可以看出,Linq To Sql在插入数据时,自动调用了事务,以防止返回的不是其插入的。

2.2
对与One : Many的关系型的,在提交One端新数据时,Linq To Sql会自动将Many端的数据一起提交。注意,是提交One端哦。比如
    var newCategory = new Category { CategoryName = "Widgets",
                                     Description = "Widgets are the customer-facing analogues " +
                                                   "to sprockets and cogs."
                                   };
    var newProduct = new Product { ProductName = "Blue Widget",
                                   UnitPrice = 34.56M,
                                   Category = newCategory
                                 };
    db2.Categories.Add(newCategory);
    db2.SubmitChanges();
2.3
而对于Many : Many的关系(关于M:M请参考上篇),就需要你从One 一个个开始,一直到Many端,自己去提交了。如:
    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.Add(newEmployee);
    db.Territories.Add(newTerritory);
    db.EmployeeTerritories.Add(newEmployeeTerritory);
    db.SubmitChanges();
3, Update
这个更简单,用Linq To Sql获取对象后,进行一系列处理后,做更新,直接调用DataContext中的SubmitChanges方法。我们来讲一个在不同DataContext之间,更新的问题。涉及到不同的DataContext,就要使用Attach方法了。在使用Attach方法时,请在其实体类的主键的Attribute上,添加IsVersion=true,比如:[Column(Storage="_PageID"...., IsVersion=true)] 。这样,另外一个DataContext才知道,该对象是否需要更新。大家需要注意的是,在更新问题上,对谁更新,就直接Attach谁。比如,有A和B两个实体,他们之间是有关系的。对A更新直接对A操作,而不是对B操作。见例子:
                nwind.Order o = null;

                using (nwind.Northwind db = new nwind.Northwind(constr))
                {
                    o = db.Orders.First();
                    o.Customer.City = "new city";
                    //db.SubmitChanges(); //此处提交是没有问题的。
                }


                using (nwind.Northwind db = new nwind.Northwind(constr))
                {
                    db.Log = Console.Out;
                    db.Orders.Attach(o, true); // 对Customer进行更新,却Attach了Order,其结果,只是在数据库中insert一个新的Customer
                    //db.Customers.Attach(o.Customer, true);  //这个是对的。
                    db.SubmitChanges();
                }
4, Delete
Delete 使用Remove方法。唯一可以讲的是,在One:Many的关系中,需要先Remove其Many端,其次才是One端。道理很简单,One端是主键呀,只要Many端还有一个和该键相关的记录,服务器是不会允许你删除该主键的。比如:
    var order =
        (from o in db.Orders
         where o.CustomerID == "WARTH" && o.EmployeeID == 3
         select o).First();

    foreach (OrderDetail od in orderDetails)
    {
        db.OrderDetails.Remove(od);
    }

    db.Orders.Remove(order);

    db.SubmitChanges();

5, Log
Log吗,顾名思义,就是日志。其记录了Linq To Sql的所有操作。我们可以将起写入文件,以备检查对数据库的操作。比如:
            StreamWriter sw = new StreamWriter("log.txt",true);
            db.Log = sw;

            var q = db.Customers.Select(c => c).ToList();

            sw.Close();
6, 更新时的冲突和事务
紫色阴影写的挺好的。引他的吧。
http://www.cnblogs.com/blusehuang/archive/2007/07/16/819677.html 事务
http://www.cnblogs.com/blusehuang/archive/2007/07/06/808529.html  冲突

PS:关于成立linq团队的提议。有人响应没?把我做为一个创始人就是了。^_^

posted on 2007-08-08 19:19 Tom Song 阅读(5067) 评论(26)  编辑 收藏 网摘 所属分类: C# 3.0Linq To Sql

Feedback

#1楼 2007-08-08 19:30 Axel      
更新在两个datacontext间:
如果父项和子项都更新都必须都Attach到datacontext,这个可以解决。在我提的级联更新问题中已提到。
但问题是一些父项可能会有多个子项,但在改变的时候不清楚是哪个已经变化,也就是不清楚哪个需要Attach.
如果把父项的所有子项都Attach一次,那么在Update中就几乎把父与子的关系都重写了一次,不合理。

  回复  引用  查看    

#2楼 2007-08-08 19:30 Axel      
PS:关于成立linq团队的提议。有人响应没?把我做为一个创始人就是了。^_^
响应

  回复  引用  查看    

单表的添加、修改、删除就不用再写代码了吧。
vs2005 不是不用了吗?vs2008怎么又改回去了呢?

  回复  引用  查看    

#4楼[楼主] 2007-08-08 19:40 宋国安      
@Axel
我觉得,是你的这个设计不合理。老婆做的美容,别人看到了老公,知道老婆做美容了吗?

  回复  引用  查看    

#5楼 2007-08-08 19:54 Axel[未注册用户]
@宋国安
@宋国安
这个比喻不合理。

你仔细想一下,如果你有一个业务对象层,其中有一个Update方法。难道在Update方法里,要把所有的子对象都Attach一次?

那么你再考虑一下,为什么在一个datacontext又是可以级联更新的呢?说明在更新时候是考虑所有父项和子项的关系的。

你可以再仔细测试一次,Attach父项后,子项的变化新是被察觉到了,但被使用了Insert方法。


@宋国安

  回复  引用    

#6楼 2007-08-08 21:21 dlinq      
@Axel
呵呵,我接着用那个例子.

我觉得这个事情,不难理解。在同一个datacontext中,是考虑了关系。那是因为,在出生地,大家都认识老公和老婆,老婆做的美容,老公就传出去了。所以,是更新。
换了一个datacontext,就跟换了地方样。把做了美容的老婆带出去,别人不记得老婆原来是什么模样,那别人就以为老公换了一个老婆.

目前还没想到合适的方案解决你的问题。想到了告诉你。

  回复  引用  查看    

#7楼 2007-08-08 22:44 Axel[未注册用户]
@dlinq
一起研究,MSDN论坛,老外也在讨论这个问题,继续跟踪。

  回复  引用    

#8楼 2007-08-09 00:03 micYng      
貌似在Linq的OR设计器中生成的类,生成到数据库中就完全不搭调了,我指的是两个类之间的关系
  回复  引用  查看    

#9楼[楼主] 2007-08-09 07:27 宋国安      
@micYng
你能给我个具体的例子吗?或者,直接把你的dbml贴出来就是了。

  回复  引用  查看    

#10楼 2007-08-09 13:36 Boler Guo      
msdn 论坛的那个帖子在讨论这个问题?
  回复  引用  查看    

#11楼 2007-08-10 23:52 micYng      
@宋国安

刚刚才看到回帖,不好意思

你可以在OR Designer中任意设计两个有关联关系的类,然后在程序里调用
db.CreateDataBase()方法,之后去数据库中查看生成的两个表,会发现表是没有关系的。

如果OR Designer中设计的类有继承关系,那么只会生成基类表,子类表一个都没有(当然现在版本的Linq还不支持单表继承,上次我在msdn论坛中问了,老外都这么说 :) )

  回复  引用  查看    

#12楼 2007-08-11 07:59 dlinq      
@micYng
啊?是吗?我回公司试试..但目前linq是单表继承呀。不支持多表继承。你可以把msdn相关帖子地址给出来吗?谢谢。

  回复  引用  查看    

#13楼 2007-08-11 15:46 micYng      
@dlinq

嗯,我有点说错了,是“多表继承”,也就是一个实体一个表这种形式的继承 :)

ps. http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1950405&SiteID=1" target="_new">http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1950405&SiteID=1

  回复  引用  查看    

#14楼 2007-08-18 19:54 lovecherry      
var q = from c in ctx.Customers select c;

foreach (var c in q)
{
c.Fax = "UK3";
}
这样的操作竟然会导致N个UPDATE。。。。。。

  回复  引用  查看    

#15楼[楼主] 2007-08-19 08:50 宋国安      
@lovecherry
是的. 在这块, linq to sql 显的效率低下.. 建议使用

db.ExecuteCommand("Update table set column = 'value' ");

  回复  引用  查看    

郁闷,在disconnect状态下删除一个父子关系中的子表,把主键设为isversion=true,只传递主键,删除的时候老是父表对应的属性已存在,无法删除!可是我删的只是子表阿!有人能告诉我怎么解决这个问题吗?

Officer o = new Officer();
o.Id = 123;
Officers.Attach(o);
Officers.Remove(o);
db.SubmitChanges();

抛出异常[System.Data.SqlClient.SqlException] = {"Cannot delete last Officer because OfficerRank exists.\r\n事务在触发器中结束。批处理已中止。"}
OfficerRank只是一个EntityRef,不明白在删除的时候还做了什么处理!

  回复  引用    

#17楼[楼主] 2007-08-21 21:00 宋国安      
@yxiaoxiong
你能把Officers和OfficerRank 的dbml贴出来吗?

这实际上是一个sql异常,抛异常的原因到像是他们的关系反了,即
Officer : OfficerRank = 1 : 1
看样子Officer 是主呢。
你贴出dbml,我就知道了,是不是这个原因了。

  回复  引用  查看    

不知道怎么贴图,不过可以列出Association的属性:
Child Property:
Name Officers
Parent Property:
Name OfficerRank
应该关系没有反。你有msn吗,我的是yxiaoxiong@hotmail.com,可以的话希望能和你沟通一下,谢谢!

  回复  引用    

#19楼 2007-10-12 09:27 int08h[未注册用户]
我想要批量删除,代码是这么写的,northwind:
var query = from detail in db.GetTable<OrderDetail>()
where detail.OrderID == 10248
select detail;
db.GetTable<OrderDetail>().RemoveAll(query);
db.SubmitChanges();

SQL PROFILER显示的SQL是:
1、一条SELECT,作为RemoveAll的参数传递时的
2、三条DELETE

我觉得,有没有可能在不是自己写SQL语句的前提下,让LINQ只生成一条DELETE,即
delete from [order details] where [orderid] in @p1
这样的?

  回复  引用    

#20楼[楼主] 2007-10-12 09:39 宋国安      
@int08h
不可以。目前in操作只支持select。像update,和delete均不支持。如果,你非要批量操作的话,建议自己写sql语句。
参阅
http://www.cnblogs.com/126/archive/2007/09/28/818149.html" target="_new">http://www.cnblogs.com/126/archive/2007/09/28/818149.html

  回复  引用  查看    

#21楼 2008-03-31 12:00 GG[未注册用户]
传统的方法同时更新多个表都是使用事务来保证数据的完整性,在linq to sql中怎么做?
  回复  引用    

兩表JOIN后绑定到DATAGRIDVIEW中,怎样直接在DATAGRIDVIEW中修改然后提交至数据库?
  回复  引用    

#23楼 2008-05-07 22:55 oldrev[未注册用户]
文章很出色,在下最近也是在尝试 LINQ+WCF但是遇到了一个问题:

在下用 WCF 包装的 LINQ to SQL 方法:

void CreateOrder(Order o)
{
ctx.Orders.InsertOnSubmit(o);
ctx.SubmitChanges();
}

其中 Order 与 OrderDetail 是一对多的关系,为何LINQ只保存了 order,没有保存 Order.OrderDetails?似乎在同一个 DataContext 下 many 端是能自动保存的。

先行谢过了。

  回复  引用    

try this:

void CreateOrder(Order o)
{
ctx.Orders.InsertOnSubmit(o);
foreach(OrderDetail in o.OrderDetails)
{
ctx.OrderDetail.InsertOnSubmit(OrderDetail );
}
ctx.SubmitChanges();
}

  回复  引用    




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 848200




相关文章:

相关链接: