LINQ to SQL使用教程


前些時間用LINQ to SQL做了一些項目,現在打算總結一下,幫助新手快速入門,並寫一些別的教程沒提到的東西。

一、LINQ to SQL和別的LINQ to XXX有什么關系?
二、延遲執行(Deferred Loading)
三、什么是LINQ to SQL?
四,看看LINQ to SQL到底干了些啥?——創建自己的工具類
五、創建一個基本查詢
六,大致掃一掃
    1,WHERE
    2,DISTINCT
    3,AVG/COUNT/SUM/MIN/MAX
    4,GROUP BY
    5,CASE WHEN
    6,INNER JOIN和OUTER JOIN
        6.1 內連接
        6.2 外連接
    7,ORDER BY
    8,EXISTS
    9,WHERE IN
    10,UNION ALL/UNION
    11,Intersect/Except
    12,Skip-Take
    13,直接執行SQL語句查詢
    14,INSERT
    15,UPDATE
    16,DELETE
    17,First/FirstOrDefault/Single
    18,字符串操作
七、查詢條件拼接
八、自動事務處理
九、關於自增的ID字段
十、關於默認值
總結

一、LINQ to SQL和別的LINQ to XXX有什么關系?

我們能接觸到的別的帶有“LINQ”字眼的東西有:LINQ to Object和LINQ to Entity Framework。它們之間的關系可以說:除了使用了相似的語法,就沒什么關系了。

LINQ to Object使用的命名空間是:System.Linq,而LINQ to SQL使用的命名空間是System.Data.Linq。

這是一個簡單的LINQ to Object的例子:

        static IEnumerable<int> FindGreaterThan5(IEnumerable<int> list)
        {
            foreach (var i in list)
            {
                if (i >= 5)
                    yield return i;
            }
        }

        static void Main(string[] args)
        {
            List<int> listTest = new List<int>{ 8, 2, 7, 9, 1, 5, 3, 4 };

            //找出所有大於等於5的數
            IEnumerable<int> result = FindGreaterThan5(listTest);
            foreach (var i in result)
            {
                Console.WriteLine(i);
            }
        }

其中FindGreaterThan5的代碼可以用LINQ to Object改為:

        static IEnumerable<int> FindGreaterThan5(IEnumerable<int> list)
        {
            return list.Where(i => i >= 5);
        }

執行的效果是完全一樣的,但要注意一點:LINQ to Object只是簡化了代碼,並非提高了效率。而且,有時候把一個foreach語句寫成一大坨LINQ表達式,其可讀性也不好,所以究竟用還是不用,這個就看你的需要了。

二、延遲執行(Deferred Loading)

像上述例子的那種使用yield return的方式返回一個可枚舉類型的函數,都會被“延遲”執行,要證明這點很簡單,改一下上面的代碼:

        static IEnumerable<int> FindGreaterThan5(IEnumerable<int> list)
        {
            foreach (var i in list)
            {
                if (i >= 5)
                    yield return i;
                else
                {
                    throw new Exception("你看不到這個異常");
                }
            }
        }

        static void Main(string[] args)
        {
            List<int> listTest = new List<int>{ 8, 2, 7, 9, 1, 5, 3, 4 };

            //找出所有大於等於5的數
            IEnumerable<int> result = FindGreaterThan5(listTest);
        }

這個程序執行沒有任何問題,你看不到異常,因為FindGreaterThan5根本沒有被執行,它只有在返回結果被用到的時候才會真正去執行(這樣做的好處后面會提到)。如果你把foreach加上,改為:

        static void Main(string[] args)
        {
            List<int> listTest = new List<int>{ 8, 2, 7, 9, 1, 5, 3, 4 };

            //找出所有大於等於5的數
            IEnumerable<int> result = FindGreaterThan5(listTest);
            foreach (var i in result)
            {
                Console.WriteLine(i);
            }
        }

那這個異常就會出現,這個地方是要十分小心的,假如你這么寫:

            IEnumerable<int> result; 
            try
            {
                result = FindGreaterThan5(listTest);
            }
            catch(Exception) //你捕捉不到異常的
            {
                return;
            }

那是捕捉不到異常的,因為它實際上發生的地方是接下來的foreach處。解決方法有兩種,一是try foreach語句,另一是“再包一層”,創建一個“Roll”函數:

        static IEnumerable<int> FindGreaterThan5Roll(IEnumerable<int> list)
        {
            return FindGreaterThan5(list);
        }

然后try這個函數。

三、什么是LINQ to SQL?

貌似講了一堆跟LINQ to SQL無關的東西,但我向你保證理解這些內容真的很重要!OK……終於到了其它一般的教程的那個開頭:什么是LINQ to SQL?

跟LINQ to Object一樣,LINQ to SQL能夠使得你對Microsoft SQL Server的訪問代碼變得簡潔,它是對ADO.net的封裝。所以它並非ADO.net的替代品,也不能帶來執行效率上的提高(用的不好反而會更低效)。這里還需要特別說明的是:

  1. 只能用於Microsoft SQL Server這套DBMS,2005版及2008版我都試過,沒問題。Oracle?MySQL?沒門;
  2. LINQ to SQL已經有了更強大和復雜的替代品,LINQ to Entity Framework,簡稱LINQ to EF或者Entity Framework,詳情請自行Google一下。

那為什么還用LINQ to SQL?一來它繼續長期有效(雖然微軟停止更新它了),二來夠用並且好用,而LINQ to EF則相對復雜。

使用LINQ to SQL其實仍然是使用ADO.net,只是LINQ to SQL幫助你生成各個查詢語句,不需要你手工來寫,這樣有什么好處?最大的好處就是:只要你的代碼編譯通過,那么就能生成正確的SQL語句,而不是報運行時錯誤,然后讓你去檢查SQL語句。

當然,好處不止這個,還有如:簡單,開發快捷,更靈活的where從句生成等。

四,看看LINQ to SQL到底干了些啥?——創建自己的工具類

前面說了,LINQ to SQL其實是“聰明”地幫你生成查詢語句,但你不能完全相信它,因為它有時候是“自作聰明”,所以你要在調試的時候看看它究竟干了些什么。我的方法是將它的生成的SQL語句打印到Debug窗口中,這個小技巧幫我找到了不少的問題,OK,這里我把我寫的這個小小的DataContext的幫助類貼出來:

    public class DebugWriter : TextWriter
    {
        public override void WriteLine(string value)
        {
            Debug.WriteLine(value);
        }

        public override void WriteLine(string format, params object[] arg)
        {
            Debug.WriteLine(format, arg);
        }

        public override Encoding Encoding
        {
            get { return Encoding.UTF8; }
        }
    }

    public static class DataContextHelper
    {
        public static void InitForModification(this DataContext dc)
        {
            dc.DeferredLoadingEnabled = false;
#if DEBUG
            dc.Log = new DebugWriter();
#endif
        }

        /// <summary>
        /// 如果一個表中的某一列引用到別的表,默認情況下LINQ to SQL會在遍歷搜索結果的時候
        /// 動態地去獲取別的表的內容,這樣就可能產生大量的SQL查詢
        /// 當把DeferredLoadingEnabled設置為false之后,LINQ to SQL則關閉這項功能,省去了大量的開銷
        /// 事實上,我們更多的時候會用到LoadWith,直接生成一條聯表查詢
        /// </summary>
        public static void InitForQuery(this DataContext dc)
        {
            dc.ObjectTrackingEnabled = false;
            dc.DeferredLoadingEnabled = false;
#if DEBUG
            dc.Log = new DebugWriter();
#endif
        }

        public static DateTime GetDbDateTime(this DataContext dc)
        {
            try
            {
                return dc.ExecuteQuery<DateTime>("SELECT GETDATE()").Single();
            }
            catch (Exception ex)
            {
                throw new Exception("Database error.", ex); //建議替換成你的自定義異常類型
            }
        }
    }

如果你對C#的擴展方法語法不是很清楚,那么可以參考我的另一篇博文:http://www.cnblogs.com/guogangj/archive/2012/03/02/2376927.html

這個幫助類很有用,把它引入到你的工程的namespace去吧(在我的工程中,我把它放入一個公共類庫中,因為好幾個工程都要引用到,避免重復,DRY,OK?)

  • InitForQuery - 用於查詢(注意看看我寫的那幾行注釋,后面會提到)
  • InitForModification - 用於增刪改
  • GetDbDateTime - 由於LINQ to SQL不直接支持GetDate函數,所以寫一個函數來獲取DBMS的時間

接下來我們還是用Northwind數據庫為例子,看看如何使用LINQ to SQL。(Northwind是一個小型數據庫例子,很多代碼都用它作為范例,在這里獲取它的創建腳本:http://northwinddatabase.codeplex.com/)

五、創建一個基本查詢

給你的工程add一個new item,叫Products.dbml,然后打開之,再如圖把Products和Categories兩張表拽進去。

然后:

        static void Main(string[] args)
        {
            ProductsDataContext db = new ProductsDataContext();
            db.Log = new DebugWriter();
            var products = from p in db.Products select p;
            foreach (var product in products)
            {
                Console.WriteLine(product.ProductName+ " (" + product.Category.CategoryName + ")");
            }
        }

這里也許你有個問題,為什么from寫在前面,而不是select,select寫在前面不是更符合SQL的習慣么?想想看,如果你能自己在5分鍾內想出來說明你比我聰明(^_^),OK,其實也跟技術本身關系不大,這樣做的原因完全是為了我們的開發環境的智能提示(intellisense),知道from什么了,后面也就有智能提示了,否則select什么?不知道。

F5,調試運行,看吧,這么一點點代碼,就能把所有商品名稱及其類型都給打印出來了,是不是很方便?——且慢!打開你的Debug窗口看看:

我的天啊,一個查詢就能完成的事情,為什么生成了這么多的SQL語句?這就是我前面所提到的DeferredLoadingEnabled這個選項,看我寫的那個幫助類的InitForQuery方法的注釋。避免這種情況的方法是關閉這個選項,並顯式告訴LINQ to SQL,你需要哪些關聯的加載內容,讓LINQ to SQL自動給你生成一個聯表查詢。我們來改進下:

        static void Main(string[] args)
        {
            ProductsDataContext db = new ProductsDataContext();
            db.InitForQuery();

            DataLoadOptions ds = new DataLoadOptions();
            ds.LoadWith<Product>(p => p.Category);
            db.LoadOptions = ds;

            var products = from p in db.Products select p;
            foreach (var product in products)
            {
                Console.WriteLine(product.ProductName+ " (" + product.Category.CategoryName + ")");
            }
        }

注意看,這次用了我前面提供的擴展方法InitForQuery,還有一個DataloadOptions選項,告訴LINQ to SQL需要哪些額外信息,這里我們需要Category信息。好,看看Debug窗口,這次只有一個SQL語句:

SELECT [t0].[ProductID], [t0].[ProductName], [t0].[SupplierID], [t0].[CategoryID], [t0].[QuantityPerUnit], [t0].[UnitPrice], [t0].[UnitsInStock], [t0].[UnitsOnOrder], [t0].[ReorderLevel], [t0].[Discontinued], [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]

LINQ to SQL聰明地為我們生成了一個left outer join,這正是我們想要的。

這個例子說明了:

  1. 我們在調試的時候得關注下LINQ to SQL到底干了些什么
  2. 使用LoadWith,而不要使用自動延遲加載

 另外,如果你不需要Product的所有列的話,你可以這樣寫:

        var products = from p in db.Products
        select new
               {
                   ProductId = p.CategoryID,
                   ProductName = p.ProductName,
                   UnitPrice = p.UnitPrice
               };

這樣生成的SQL語句是不太一樣的,自己觀察下,但這樣生成出來的對象,是不能用於Insert和Update的,這個要注意一下。

六,大致掃一掃

寫一篇無微不至的教程並非本文目的,這個已經有了不少好的教程,所以接下來就稍微簡略一些,希望能對大家起到一定的拋磚引玉的作用。

1,WHERE

把1994年后雇佣的,或者Title中包含“經理”的員工選出來

            var result =
                from e in db.Employees
                where e.HireDate >= new DateTime(1994, 1, 1) || e.Title.Contains("Manager")
                select e;

2,DISTINCT

獲取有交易的客戶ID(重復的去掉)

var result = (from o in db.Orders select new { CustomerID = o.CustomerID }).Distinct();

3,AVG/COUNT/SUM/MIN/MAX

取得產品價格的平均值

            var result = db.Products.Select(p => p.UnitPrice).Average();
            var result = db.Products.Average(p => p.UnitPrice);
            var result = (from p in db.Products select p.UnitPrice).Average();

從這也能看出寫法並不是唯一的。其它集合操作函數也是類似的。

4,GROUP BY

選出每一類的最貴產品、最便宜產品及產品均價

            var result =
                from p in db.Products
                group p by p.CategoryID into g
                select new
                {
                    g.Key,
                    Amount = g.Count(),
                    MaxPrice = g.Max(item => item.UnitPrice),
                    MinPrice = g.Min(item=>item.UnitPrice),
                    AveragePrice = g.Average(item=>item.UnitPrice)
                };

5,CASE WHEN

            var q = from c in db.Customers
                    select new
                    {
                        Name = c.ContactName,
                        Address = new
                        {
                            City = c.City,
                            Region = c.Region == null ? "Unknown" : c.Region
                        }
                    };

翻譯為:

SELECT [t0].[ContactName] AS [Name], [t0].[City],
    (CASE
        WHEN [t0].[Region] IS NULL THEN CONVERT(NVarChar(15),@p0)
        ELSE [t0].[Region]
     END) AS [Region]
FROM [dbo].[Customers] AS [t0]

6,INNER JOIN和OUTER JOIN

想用LINQ to SQL直接寫聯表查詢是很麻煩的,不過這也是唯一一個相比直接用SQL來得更麻煩的地方。

6.1 內連接

            var result = from d in db.Order_Details
                         join o in db.Orders on d.OrderID equals o.OrderID
                         select new {d.OrderID, d.ProductID, d.UnitPrice, o.RequiredDate};

翻譯為

SELECT [t0].[OrderID], [t0].[ProductID], [t0].[UnitPrice], [t1].[RequiredDate]
FROM [dbo].[Order Details] AS [t0]
INNER JOIN [dbo].[Orders] AS [t1] ON [t0].[OrderID] = [t1].[OrderID]

另外一種更緊湊的寫法

            var result = from d in db.Order_Details
                 from o in db.Orders.Where(item=>item.OrderID==d.OrderID)
                 select new { d.OrderID, d.ProductID, d.UnitPrice, o.RequiredDate };

翻譯為

        SELECT [t0].[OrderID], [t0].[ProductID], [t0].[UnitPrice], [t1].[RequiredDate]
        FROM [dbo].[Order Details] AS [t0], [dbo].[Orders] AS [t1]
        WHERE [t1].[OrderID] = [t0].[OrderID]

執行效果一樣的

6.2 外連接

             var result = from d in db.Order_Details
                          join o in db.Orders on d.OrderID equals o.OrderID into temp
                          from p in temp.DefaultIfEmpty()
                          select new {d.OrderID, d.ProductID, d.UnitPrice, p.RequiredDate};

另一種更緊湊的寫法是:

            var result = from d in db.Order_Details
                         from o in db.Orders.Where(item => item.OrderID == d.OrderID).DefaultIfEmpty()
                         select new {d.OrderID, d.ProductID, d.UnitPrice, o.RequiredDate };

翻譯為

SELECT [t0].[OrderID], [t0].[ProductID], [t0].[UnitPrice], [t1].[RequiredDate] AS [RequiredDate]
FROM [dbo].[Order Details] AS [t0]
LEFT OUTER JOIN [dbo].[Orders] AS [t1] ON [t1].[OrderID] = [t0].[OrderID]

7,ORDER BY

正序的情況

    var result = from p in db.Products orderby p.UnitPrice select p;

逆序的情況

    var result = from p in db.Products orderby p.UnitPrice descending select p;

也可以選擇多列排序

    var result = from p in db.Products orderby p.UnitPrice, p.UnitsInStock select p;

上面的句子也可以這么寫,一樣的

    var result = from p in db.Products.OrderBy(item => item.UnitPrice).ThenBy(item => item.UnitsInStock) select p;

8,EXISTS

選出沒有訂單的客戶

    var result = from c in db.Customers where !c.Orders.Any() select c;

其實Any還可以帶條件參數哦。

9,WHERE IN

查看指定的幾個客戶的訂單

var result = (
    from o in db.Orders
    where (
    new string[] { "AROUT", "BOLID", "FISSA" })
    .Contains(o.CustomerID);

10,UNION ALL/UNION

Concat不會去除重復項目,相當於SQL的Union All;而Union會去除重復項,相當於SQL的Union

看看有來自哪些國家的客戶和雇員

            var q = (
                     from c in db.Customers
                     select c.Country
                    ).Union(
                     from e in db.Employees
                     select e.Country
                    );

11,Intersect/Except (是用復合查詢加EXIST實現的)

            var q = (
                     from c in db.Customers
                     select c.Country
                    ).Intersect(
                     from e in db.Employees
                     select e.Country
                    );

查詢顧客和職員同在的國家,Interset其實是用一個復合查詢實現的。

            var q = (
                     from c in db.Customers
                     select c.Country
                    ).Except(
                     from e in db.Employees
                     select e.Country
                    );

去除顧客和職員同在的國家。

12,Skip-Take(是用ROW_NUMBER()函數實現的)

獲取從第21個產品開始的10個產品。

            var q = (
                        from p in db.Products
                        select p
                    ).Skip(20).Take(10);

13,直接執行SQL語句查詢

如果發現查詢語句很難寫,(或者寫出來LINQ會傻乎乎地生成低效的多次執行)可以考慮直接使用SQL語句,但缺點就是失去了編譯器檢查的功能,並且得自己構建好一個類,用於存放數據(如果此類還沒有的話)。

IEnumerable<Employee> emps = db.ExecuteQuery<Employee>("select * from Employees");

PS:直接寫“*”可不太好

14,INSERT

注意:如果指定的ID存在,則會自動執行Update,而不是Insert(LINQ to SQL是不是很“智能”?都有些自作聰明了)

Region nwRegion = new Region()
{
    RegionID = 32,
    RegionDescription = "Rainy"
};
db.Regions.InsertOnSubmit(nwRegion);
db.SubmitChanges();

15,UPDATE

真奇怪,update和insert居然不同,沒有一個專門的Update方法,而是直接取出數據庫的條目,然后修改其屬性,在SubmitChanges,當然,條目的類型必須是dbml自動幫我們生成的類型,不能是自定義的。這是簡單的Update的例子:

Customer cust = db.Customers.First(c => c.CustomerID == "ALFKI");
cust.ContactTitle = "Vice President";
db.SubmitChanges();

這是Update多條的例子:

var q = from p in db.Products
        where p.CategoryID == 1
        select p;
foreach (var p in q)
{
    p.UnitPrice += 1.00M;
}
db.SubmitChanges();

16,DELETE

先選后刪

OrderDetail orderDetail =
    db.OrderDetails.First
    (c => c.OrderID == 10255 && c.ProductID == 36);
db.OrderDetails.DeleteOnSubmit(orderDetail);
db.SubmitChanges();

17,First/FirstOrDefault/Single(是用TOP實現的)

都是取出一條記錄,區別是:

  • First – 至少有一條,否則拋異常
  • FirstOrDefault – 如果一條都沒有,則返回默認值(對象的話默認值就是null)
  • Single – 有且只有一條,否則拋異常

選出ID為1的雇員:

Employee theveryemp = db.Employees.Single(item => item.EmployeeID == 1);

這個語句會立即執行,如果獲取不到或者不止一條,則拋出異常。

18,字符串操作

以下這些LINQ中對字符串操作的部分都會被聰明地轉變為相關的SQL語句,而不是使用C#代碼來操作。具體會被轉換成什么,大家動手試試看。

Location = c.City + ", " + c.Country
p.ProductName.Length < 10
c.ContactName.Contains("Anders")
c.ContactName.IndexOf(" ")
c.ContactName.StartsWith("Maria")
c.ContactName.EndsWith("Anders")
p.ProductName.Substring(3);
e.HomePhone.Substring(6, 3) == "555"
e.LastName.ToUpper()
c.CategoryName.ToLower()
e.HomePhone.Substring(0, 5).Trim()
e.HomePhone.Insert(5, ":")
e.HomePhone.Remove(9)
e.HomePhone.Remove(0, 6)
s.Country.Replace("UK", "United Kingdom")

七、查詢條件拼接

客戶端在查詢的時候往往會附帶一些查詢條件,例如商品名稱模糊查詢,日期范圍,商品類型范圍,當然還有分頁等等。我們通常把這些查詢條件封裝到一個對象中去,然后讓服務器來解釋這個對象並拼接SQL查詢語句。拼接SQL語句是極其容易出錯的事情,而且檢查起來還比較費勁,因為SQL語句寫起來並不像C#代碼那樣可以自動格式化。如今使用LINQ to SQL這些問題就不存在了。

var q = from p in db.Products select p;
if(條件A)
{
    q.Where(p=>p.XXX==A);
}
if(條件B)
{
    q.Where(p=>p.YYY!=B);
}
if(條件C)
{
    q.Where(p=>p.ZZZ.Contains(C));
}
//……

回頭想想本文初所提到的延遲執行,到這里你應該知道為什么需要延遲執行了吧,就是為了方便你拼接這些LINQ表達式,如果每個select或者where都執行一次,那可是會帶來嚴重的性能問題的。

八、自動事務處理

也許你發現了,對於增刪改,都是在SubmitChanges的時候執行,而且一次SubmitChanges,能改多個表多條記錄,那事務在哪里?其實LINQ to SQL已經自動幫我們封裝好事務了,在執行的過程中,一旦有一步失敗,操作將會回滾,這個完全不需要我們擔心。

九、關於自增的ID字段

按設計慣例,每張表都應該有一個自增的ID字段,對於這個字段,其值總是由數據庫自動生成的,所以我們在Insert一行的時候,從來不需要指定其ID。例如,我們查看ProductID的屬性列表,有個叫Auto Generated Value的屬性,其值為True,即代表這個字段的值是DBMS自動生成的,你不需要指定。

那么我們插入了一條記錄之后,我們想取回這個自動生成的ID的值,怎么辦呢?按以前的做法是:

select scope_identity()

現在的做法是在SubmitChanges()之后直接通過插入的對象帶出這個自動生成的ID的值:

            Category newCategory = new Category {CategoryName = "Fruit", Description = "Fruits..."};
            db.Categories.InsertOnSubmit(newCategory);
            db.SubmitChanges();
            Console.WriteLine(newCategory.CategoryID);

那現在有這么種情況,我要添加一個訂單,同時給這個訂單添加若干條明細,怎么辦?這個看起來有點難,難在哪里?你要添加明細,你就必須知道主檔的ID,但在SubmitChanges之前,你是拿不到主檔的ID的;如果你在Insert了主檔之后就Submit,那一旦在Insert明細的時候失敗,你就無法回滾了。

OK,其實這么想的話還是按照舊的思路,LINQ to SQL是一套ORM,我們應該直接指定其對象的關系,而不是去關心ID的值是多少,這是正確的做法:

            Order newOrder = new Order { CustomerID = "ALFKI", ShipAddress = "Shanghai ..." };
            Order_Detail newDetail1 = new Order_Detail { Discount = 1.0f, ProductID = 1, Quantity = 1, UnitPrice = 9.0M };
            Order_Detail newDetail2 = new Order_Detail { Discount = 0.9f, ProductID = 2, Quantity = 2, UnitPrice = 5.0M };
            newOrder.Order_Details.Add(newDetail1);
            newOrder.Order_Details.Add(newDetail2);
            db.Orders.InsertOnSubmit(newOrder);
            db.SubmitChanges();

十、關於默認值

數據庫的表中的很多字段都帶有默認值,前面提到的自增ID就是一個例子,但大多字段跟ID不同的是:ID是強制的並且一定是DBMS自動分配的,而這些帶默認值的字段則不一定強制,並且允許由用戶傳入值。如果把這些帶默認值的字段跟自增ID一樣,設置其“Auto Generated Value”屬性為True的話,我們就沒辦法設置它的值了,它的值總是由DBMS自動分配,而事實上,我們想要的結果是:當我沒有給值的時候使用默認值,當我有給值的時候,使用我的值。很不幸,LINQ to SQL做不到,我嘗試過很多種方法,結果很明確,就是做不到!這也許算是LINQ to SQL的一個缺陷吧。

所以,使用LINQ to SQL的話就把DBMS的默認值忽略掉吧,每次都手工把值傳進去好了……

總結

感謝你看完本文,本文肯定不是最全面的對LINQ to SQL的技術文章,但真心是本人實戰的總結,如果上面提到的內容你都理解,那LINQ to SQL你也就掌握差不多了……呃,我是說即便你再遇到什么問題,你也肯定有解決的思路了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM