2017-12-28 22 views
-1

WCF에서 거대한 행을 얻고 있습니다. 그런 다음 1을 로컬 SQLite DB에 삽입하고 싶습니다. 이를 위해 SQLItePCL을 사용하고 있습니다. 디버거를 사용하여 약 900 개의 행이 있음을 감지하고 for 루프를 시작하여 하나씩 차례로 삽입했습니다. 하지만 그것은 하나의 행을 삽입하는 것이 아니며 로더 (데이터가 삽입 될 때 표시하는 로더를 사용했습니다)가 무한 시간로드됩니다. LocalState 폴더에서 로컬 DB의 크기를 확인하고 있지만 증가하지 않고 있습니다. 즉, 아무 일도 일어나지 않고 있지만 내 응용 프로그램도 충돌하지 않는 것입니다.SQLitePCL을 사용하여 SQLite의 WCF에서 900 (많은 수의 행)을 삽입하지 못합니다.

using (SQLiteConnection Conn = new SQLiteConnection("BerriaDb.db")) 
{ 
    List<BerrianWebService.FoodCode> ResponseData = wsResponse.data.ToList(); 
    for (int i = 0; i < ResponseData.Count; i++) 
    { 
     string sqlInsFoodCode = @"INSERT INTO [FoodCode] ([ID], [Item_Name], [Code], [ItemID], [FoodCodeID], [Brief_Descriptor], [Full_Descriptor], [Guidesheet], [REG], [Status], [syncDate]) VALUES (" + ResponseData[i].ID + ", '" + ResponseData[i].Item_Name + "', '" + ResponseData[i].Code + "', '" + ResponseData[i].ItemID + "', '" + ResponseData[i].FoodCodeID + "', '" + ResponseData[i].Brief_Descriptor + "', '" + ResponseData[i].Full_Descriptor + "', '" + ResponseData[i].Guidesheet + "', '" + ResponseData[i].REG + "', '" + ResponseData[i].Status + "', '" + LastSyncDateTime + "')"; 
     using (ISQLiteStatement connStatement = Conn.Prepare(sqlInsFoodCode)) 
     { 
      connStatement.Step(); 
     } 
    } 
} 

여기에 문제가있을 수 있습니다 무엇 : 행 번호는 아래 (200)보다 작은 데이터를 삽입하기위한 코드입니다 때 모든 것이 잘입니까? 여기에 StringBuilder를 사용해야합니까? 어떠한 제안? 감사합니다

편집 : 나는 주된 문제가 실제로 ResponseData가 (900과 같이) 커지면 각 명령 내에서 루프를 돌릴 수없고 그 값을 평가할 수 없다는 것을 알아 냈습니다. SQL 문자열을 생성하지만 정적 값을 제공 할 때 잘 작동합니다. 어떤 도움이 필요합니까?

+1

당신이'connStatement.Step()를 변경하는 경우,'var에 밥 = connStatement.Step()'로,'무엇을 100, 199, 200, 201 행의'bob' 값입니까? – mjwills

+0

"문자열 sqlInsFoodCode = .........."이후 Visual Studio로 돌아 오지 않는 이유는 알 수 없습니다. ( – Kibria

+0

예, 이미 완료되었지만 넘어 설 수는 없습니다. "문자열 sqlInsFoodCode = @ "INSERT INTO 명령이 여기에 오는데 아무 것도 잘못하지 않았습니다." "로더가 무한 시간로드되기 시작합니다. – Kibria

답변

0

코드는 SQL 삽입 공격에 개방되어 있으며 SQL 문 작성 및 후속 단계 호출이 실패하는 이유를 설명 할 수 있습니다.

대신 Bind 매개 변수 및 해당 Bind 호출을 사용하여 Prepared statement을 최대한 활용하십시오. 다음과 같이 코드를 적응 :

var LastSyncDateTime = DateTime.Now; 

using (SQLitePCL.SQLiteConnection Conn = new SQLitePCL.SQLiteConnection("BerriaDb.db")) 
{ 

    // use Bind Parameters, that are the @id, @item_name etc 
    string sqlInsFoodCode = @" 
    INSERT INTO [FoodCode] (
     [ID], 
     [Item_Name], 
     [Code], 
     [ItemID], 
     [FoodCodeID], 
     [Brief_Descriptor], 
     [Full_Descriptor], 
     [Guidesheet], 
     [REG], 
     [Status], 
     [syncDate]) 
    VALUES (
     @id, 
     @item_name, 
     @code, 
     @itemid, 
     @foodcodeid, 
     @brief_descriptor, 
     @full_descriptor, 
     @guidesheet, 
     @reg, 
     @status, 
     @lastsynctime)"; 

    // we can now re use this statement for all inserts 
    using (ISQLiteStatement connStatement = Conn.Prepare(sqlInsFoodCode)) 
    { 
     List<FoodCode> ResponseData = wsResponse.data.ToList(); 
     for (int i = 0; i < ResponseData.Count; i++) 
     { 
      // just to be sure no values from last time are kept 
      connStatement.ClearBindings(); 

      // bind the values from the Responsedata to the parameters 
      connStatement.Bind("@id", ResponseData[i].ID); 
      connStatement.Bind("@item_name", ResponseData[i].Item_Name); 
      connStatement.Bind("@code", ResponseData[i].Code); 
      connStatement.Bind("@itemid", ResponseData[i].ItemID); 
      connStatement.Bind("@foodcodeid", ResponseData[i].FoodCodeID); 
      connStatement.Bind("@brief_descriptor", ResponseData[i].Brief_Descriptor); 
      connStatement.Bind("@full_descriptor", ResponseData[i].Full_Descriptor); 
      connStatement.Bind("@guidesheet", ResponseData[i].Guidesheet); 
      connStatement.Bind("@reg", ResponseData[i].REG); 
      connStatement.Bind("@status", ResponseData[i].Status); 
      connStatement.Bind("@lastsynctime", LastSyncDateTime.ToString()); 

      // alwaws take the result ... 
      var result = connStatement.Step(); 
      // ... and check if we're OK, (done in this case) 
      if (result != SQLiteResult.DONE) 
      { 
       // if not, yell at the caller 
       throw new Exception(String.Format("ResponseData[{0}].ID('{1}') not inserted. Result: {2}", i, ResponseData[i].ID, result)); 
      } 
      // enable the re-use of the prepared statement, so call Reset 
      connStatement.Reset();    
     } 
    } 
} 

100 개 행을 삽입

내 상자에 약 10 초 정도 그 시간은 더 행에 대해 선형 적이다.

테스트 중에 ":memory:"을 데이터베이스 이름으로 사용할 수 있습니다. 그러면 디스크에 훨씬 더 빨리 쓰는 메모리 내장 저장소가 생깁니다.

이 경우 사람이 이것을을 TestRun을주고 싶어, 여기에 지원하는 클래스입니다 :

// this will be an instance in wsResponse 
class response 
{ public response() { 
     data = Enumerable.Range(1,10).Select(id => new FoodCode(id)).ToArray(); 
    } 
    public FoodCode[] data; 
} 

class FoodCode 
{ 
    public int ID; 
    public string Item_Name; 
    public string Code; 
    public int ItemID; 
    public int FoodCodeID; 
    public string Brief_Descriptor; 
    public string Full_Descriptor; 
    public string Guidesheet; 
    public string REG; 
    public string Status; 

    public FoodCode() 
    { 
     // populate all fields with random stuff 
     foreach(FieldInfo fi in this.GetType().GetFields()) 
     { 
      if (fi.FieldType == typeof(string)) { 
       fi.SetValue(this, GenString(fi.Name, ID)); 
      } else 
      { 
       fi.SetValue(this, rnd.Next(1,255)); 
      } 
     } 
    } 

    public FoodCode(int id):this() 
    { 
     ID = id; 
    } 

    static Random rnd = new Random(); 
    // generate a random string 
    private string GenString(string name, int id) 
    { 
    var result = new string[] { name, id.ToString(), new String(Enumerable.Range(0,5000).Select(_=> (char) rnd.Next(33,127)).ToArray())}; 
    return String.Join("-", result); 
    } 

}