2011-12-15 24 views
7

Nhìn này:C#: Tôi có nên sử dụng "if" thay vì "while" khi Reader chỉ có một phần tử không?

myReader = cmd.ExecuteReader(); 
if (myReader.HasRows) 
{ 
    while (myReader.Read()) 
    { 
     info = myReader.GetString("info"); 
     ... 
    } 
} 
..... 

Vâng, tôi không biết tại sao, nhưng khi tôi biên dịch nó và sử dụng nó trong một máy tính khác, nó phát nổ vì khi. Nhưng nếu tôi làm:

myReader = cmd.ExecuteReader(); 
if (myReader.HasRows) 
{ 
    if (myReader.Read()) <------------------- NOTICE THE IF 
    { 
     info = myReader.GetString("info"); 
     ... 
    } 
} 
..... 

... sẽ hoạt động. Lưu ý rằng tôi biết rằng tôi sẽ chỉ có chỉ một phần tử mỗi lần, vì logic của chương trình.

Bạn có thể thực hiện việc này không? Trông có vẻ kinh khủng và có thể không phải là một thực hành tốt (?). Tôi đang yêu cầu định hướng của bạn.

EDIT: Sẽ đăng một số mã hơn, bởi vì có thể giúp đỡ trong việc tìm kiếm lỗi của tôi:

while (myReader.Read()) 
{ 
    info = myReader.GetString("info"); 
    ... 


    /** 
     * Write the relevant info in the LOGS 
     */ 
     connection.Close();  <---------- :S 
     connection.Open();  <---------- if I don´t do this I got some problems with the connection!!!! 
     string queryLog = "INSERT INTO ....; 
     MySqlCommand cmdLog = new MySqlCommand(queryLog, connection); 
     cmdLog.ExecuteNonQuery(); 
} 
+5

"Phát hiện" có nghĩa là gì? – SLaks

+0

Dường như có một vấn đề cơ bản mà bạn có lẽ nên hiểu hơn là chỉ tránh nó như bạn có. Explode có nghĩa là gì? Là một ngoại lệ ném? Ngoại lệ là gì? –

+0

Là một ngoại lệ-Tôi không có trong tay ngay bây giờ, nhưng là về các kết nối. Tôi đã chỉnh sửa bài đăng để thêm một số mã về nó. – Kani

Trả lời

13

Tôi làm điều này bất cứ khi nào tôi đang mong đợi chỉ một hàng, không có gì sai với nó.

Nếu tôi đang mong đợi một giá trị duy nhất, tôi sử dụng ExecuteScalar để thay thế. Điều này sẽ đơn giản trả về giá trị được giữ trong cột đầu tiên của hàng đầu tiên của kết quả trả về, theo cách thông thường, tập hợp chỉ có một cột và một hàng, do đó bạn không có xu hướng chú ý đến điều này.

Ngoài ra tôi có xu hướng không bận tâm với if (myReader.HasRows)if (myReader.Read()) bao gồm điều này.

Tôi không biết tại sao số while khiến bạn gặp sự cố ... Tôi chỉ đưa ra ý kiến ​​của mình về việc sử dụng if trên while nếu bạn không mong đợi nhiều hàng.

+0

Thú vị. Cho đến bây giờ, tôi đã không nhận ra ExecuteScalar có thể trả về một chuỗi. Tôi luôn luôn nghĩ về một vô hướng như một con số bởi vì nó thường được sử dụng theo cách đó trong vật lý và toán học, và ExecuteScalar thường được sử dụng để trả về một số. –

+0

@EricJ. Vâng, cách đặt tên của phương pháp không hoàn toàn tuyệt vời, nhưng cá nhân tôi đọc vô hướng là "giá trị đơn", vì vậy tôi chưa bao giờ xem điểm quan sát của bạn cho đến bây giờ :-) –

+0

Nó phù hợp với định nghĩa perl của "vô hướng" , mà tôi hiểu có nghĩa là "nguyên tố đơn lẻ" trái ngược với một danh sách. – recursive

3

Không sử dụng while, bạn không thể phát hiện điều kiện bất ngờ mà người đọc có nhiều yếu tố hơn bạn nghĩ. Hệ thống thay đổi theo thời gian và các giả định như "người đọc sẽ chỉ có một yếu tố" có xu hướng trở nên không chính xác khi hệ thống phát triển.

Nếu điều quan trọng đối với chức năng của hệ thống hoặc chức năng của mã bạn đang viết có giả định được nhúng vào, tôi vẫn sẽ sử dụng while và bao gồm một số mã chỉ xây dựng gỡ lỗi xác nhận rằng bạn thực sự chỉ nhận được một phần tử.

+0

Điểm tốt và không thực sự bị tổn thương khi sử dụng Trong khi thay vì Nếu. Không có vấn đề về hiệu suất mà tôi biết trong khu vực này –

+0

Cảm ơn bạn. Tôi tin rằng bạn đúng rằng hệ thống có thể thay đổi. Tôi sẽ nói chuyện với tôi các trường đại học để xem là các yêu cầu sẽ không thay đổi, ** bao giờ ** – Kani

3

Tôi nghĩ rằng việc sử dụng if làm cho nó rõ ràng với người đọc mã của bạn mà bạn mong đợi chính xác một mục. Bạn có thể thêm một khẳng định sau khi kết thúc để bắt lỗi logic, như thế này:

if (myReader.Read()) { 
    info = myReader.GetString("info"); 
    ... 
} 
Debug.Assert(!myReader.Read(), "Reader has more elements than expected."); 
+0

+1 Cuộc gọi tốt trên xác nhận. –

-5

mã này bạn nên thêm những điều sau đây để có được nó để trả lại hoặc không trả lại hàng

myReader = cmd.ExecuteReader(); 
    myReader.Read(); 
    if (myReader.HasRows) 
    {  
    while (myReader.Read()) 
    { 
     info = myReader.GetString("info"); 
    } 
    } 
+5

Mã đó không phải là tuyệt vời ... bạn lặp lại trình đọc của bạn trước khi bạn bắt đầu đọc để bạn luôn bỏ lỡ giá trị trả về đầu tiên. OP chỉ quan tâm đến một hàng, vì vậy điều này sẽ không thực sự đủ. –

+0

Tôi hiểu rằng .. Tôi đã chỉ cho anh ta những gì anh ấy có thể làm .. đó là vào anh ta để hiểu được để sử dụng .. đọc từ mã của mình bạn không thể nói nếu anh ta sẽ trở lại một hàng hoặc nhiều .. vì vậy bạn xuống bỏ phiếu là một chút sớm .. – MethodMan

+8

Các downvote đã được bảo hành theo ý kiến ​​của tôi, ngay cả với nhiều hàng bạn đang bỏ qua hàng đầu tiên cho mỗi resultset trả về - Tôi chưa bao giờ thấy điều này cần thiết trong tự nhiên và sẽ gây phiền nhiễu để gỡ lỗi. OP cũng đề cập rằng anh ta hy vọng "chỉ một phần tử". Bạn cũng đang hiển thị mã anh ấy đã có, vì vậy tôi không thấy giá trị nào đã được thêm vào ngoài lỗi tiềm ẩn. –

0

Tôi nghĩ bạn nên vẫn còn sử dụng trong khi ngay cả khi bạn đang mong đợi chỉ có một hàng sau đó sử dụng LINQ để thể hiện mong đợi hàng duy nhất khi bạn gọi nó. Bạn có thể sử dụng một coroutine để thể hiện điều này một cách độc đáo.

public IEnumerable<string> GetInfo() 
{ 
    // make command and reader... 
    while(reader.Read() 
    { 
     yield return reader.GetString("info"); 
    } 
} 

Sau đó, người gọi nên sử dụng phương thức Đơn() để thể hiện kỳ ​​vọng chỉ có một hàng.

var info = GetInfo().Single(); 
1

Bạn có định đoạt kết nối và đầu đọc đúng cách không? Sử dụng Using, như:

using (myReader = cmd.ExecuteReader()) 
{ 
    if (myReader.HasRows) 
    { 
     while (myReader.Read()) 
     { 
      info = myReader.GetString("info"); 
      ... 
     } 
    } 
} 
3

Bạn thực sự có thể sử dụng if, nhưng bạn cũng nên có thể sử dụng while trong một trường hợp như thế này mà không có lỗi. Chỉ cần thay đổi nó thành if sẽ hoạt động, nhưng nếu bạn làm điều tương tự trong trường hợp bạn có thể có nhiều hơn một hàng, bạn sẽ nhận được cùng một lỗi. Do đó, điều quan trọng là phải xem xét lý do tại sao ngoại lệ lại xảy ra.

Hãy kiểm tra mã của bạn:

while (myReader.Read()) 
{ 
    info = myReader.GetString("info"); 
    ... 

Cho đến nay rất tốt.

 /** 
     * Write the relevant info in the LOGS 
     */ 
     connection.Close();  <---------- :S 
     connection.Open();  <---------- if I don´t do this I got some problems with the connection!!!! 

Oh uh! Bạn đã khắc phục "một số vấn đề", nhưng bạn có biết những "vấn đề" đó là gì không? Nó thực sự ở đây mà bạn đã phá vỡ while của bạn.

Tại điểm mà tôi đã nói "cho đến nay rất tốt", đây là những gì bạn đã xảy ra:

  1. Bạn có một đối tượng kết nối. Nó là "mở".
  2. Bạn có đối tượng trình đọc dữ liệu. Nó đang lấy dữ liệu từ kết nối.

Hiện tại, theo thông số IDbCommand.ExecuteReader (http://msdn.microsoft.com/en-us/library/68etdec0.aspx), kết nối được sử dụng cho người đọc đó. Bạn không được phép làm bất cứ điều gì với kết nối đó ngoại trừ việc đóng nó, cho đến khi bạn gọi Close() trên đầu đọc, hoặc rõ ràng hoặc thông qua Dispose() như sẽ xảy ra nếu người đọc được chỉ định trong khối using.

Hiện tại, điều này không phải lúc nào cũng đúng. Một thực hiện nhất định luôn được phép cung cấp cho bạn nhiều hơn một lời hứa giao diện (cho dù theo chữ ký giao diện hay theo tài liệu). Nhiều triển khai sẽ "giải phóng" kết nối để tái sử dụng khi Read() trả về false (chỉ có một tôi đã thực hiện), SQLServer 2005 có một tùy chọn MultipleActiveResultSets=True cho phép cùng một kết nối đồng thời hỗ trợ nhiều hơn một trình quản lý dữ liệu cùng một lúc (có những nhược điểm). Tuy nhiên, những nới lỏng của các quy tắc này sang một bên, điều duy nhất chúng tôi chắc chắn có thể làm với kết nối của chúng tôi ngay bây giờ, là gọi Close().

Vì lý do này, khi bạn cố gắng gọi cmdLog.ExecuteNonQuery() bạn gặp "một số vấn đề" dưới dạng thông báo lỗi, vì bạn đang cố gắng sử dụng kết nối đang được sử dụng cho một thứ khác.

Vì vậy, bạn "cố định" điều này bằng cách làm điều duy nhất bạn có thể làm với kết nối - đóng nó - và sau đó mở lại. Đối với tất cả ý định và mục đích, bạn sẽ có một kết nối hoàn toàn mới!

Bây giờ, khi bạn lặp lại số while, nó gọi lại là myReader.Read().Phương thức này đọc từ luồng dữ liệu đến từ kết nối và trả về false (nếu không có thêm kết quả) hoặc tạo một tập hợp các trường dựa trên những gì nó đọc từ luồng đó.

Nhưng không phải vì bạn đã đóng kết nối đó. Vì vậy, nó "phát nổ" với một ngoại lệ bởi vì bạn đang cố gắng đọc từ một kết nối khép kín và điều đó không thể thực hiện được.

Bây giờ, khi bạn thay thế while này với một if bạn không bao giờ trở lại hoạt động Read() bạn muốn vỡ, vì vậy mã làm việc một lần nữa.

Nhưng bạn sẽ làm gì nếu bạn cần làm điều này với một kết quả có nhiều hàng?

Bạn có thể sử dụng MARS. Xem http://msdn.microsoft.com/en-us/library/h32h3abf%28v=vs.80%29.aspx cho điều đó. Thành thật mà nói tôi muốn tránh nó trừ khi cái gì đó tăng hơn thế này từ nó; nó có một số hàm ý tinh tế có thể gây nhầm lẫn nếu bạn đánh chúng.

Bạn có thể trì hoãn hoạt động tiếp theo của mình cho đến khi người đọc đóng cửa. Trong trường hợp này, điều này sẽ chỉ là một tiết kiệm nhỏ bằng cách bỏ đi không cần thiết Open()Close():

using(myReader = cmd.ExecuteReader()) 
{ 
    while(myReader.Read()) 
    { 
    string info = myReader.GetString("info"); 
    /* Do stuff */ 
    using(SqlConnection logConn = new SqlConnection(...)) 
    { 
     logConn.Open(); 
     string queryLog = "INSERT INTO ....; 
     MySqlCommand cmdLog = new MySqlCommand(queryLog, connection); 
     cmdLog.ExecuteNonQuery(); 
    } 
    } 
} 

Bây giờ, nó có vẻ lãng phí để có hai kết nối chứ không phải ai ở đây. Tuy nhiên:

  1. Tạo kết nối tạo kết nối khá rẻ.
  2. Chúng tôi sẽ phát hành lại hồ bơi ngay khi chúng tôi thực hiện ExecuteNonQuery(), vì vậy nếu có rất nhiều điều này xảy ra trên nhiều chuỗi, tổng số kết nối khi di chuyển sẽ ít hơn gấp đôi số lượng chủ đề. Mặt khác, nếu chỉ có một chủ đề làm điều này, thì chỉ có một kết nối phụ, vì vậy ai quan tâm.
  3. Đóng kết nối khi nó là một phần thông qua việc cung cấp trình quản lý dữ liệu có thể đã làm sạch thêm trước khi quay lại hồ bơi, vì vậy bạn cũng có thể sử dụng hai kết nối (tôi không biết về SQLServer, nhưng tôi biết rằng một số cơ sở dữ liệu cần nhiều công việc hơn trước khi chúng có thể quay lại hồ bơi trong những trường hợp như vậy).

Tóm lại, bạn không thể sử dụng kết nối mà không sử dụng lại nó, đó là lỗi hết hạn. Bạn thường không thể có nhiều thao tác trên cùng một kết nối tại một thời điểm. Sử dụng tùy chọn MultipleActiveResultSets với SQLServer2005 cho phép bạn làm điều đó, mặc dù nó có các biến chứng. Nói chung, nếu bạn muốn làm nhiều hơn một điều DB tại một thời điểm, bạn nên sử dụng nhiều hơn một kết nối.

Tất cả những gì đã nói, bạn vẫn có thể sử dụng if hơn while, nhưng làm điều đó bởi vì đó là những gì có ý nghĩa vào thời điểm đó, chứ không phải để sửa chữa một lỗi bạn không hiểu và có nó trở lại một thời gian khi if isn không phải là một lựa chọn.

+0

Tuyệt vời! Cảm ơn bạn. – Kani

Các vấn đề liên quan