2017-09-05 36 views
0

我在我的應用程序中使用SQLite(通過System.Data.SQLite包)。現在所有的插入,查詢等操作,通過使用字符串發送命令,例如做:在C#中處理SQLite - 推進作爲命令傳遞字符串操作

 SQLiteCommand command = new SQLiteCommand(comStr, db); 

其中comStr - 是一個字符串變量保存命令。

我可以使用其他選項而不是字符串嗎?或者,字符串是從.NET處理SQL查詢時應該使用的正確方法嗎?

問題是,使用字符串可能會相當混亂,例如我有一些用戶可以設置的過濾器。使用字符串操作命令 - 儘管作品 - 讓我感覺非常脆弱:

public string GetFilterString() 
    { 
     string fil1 = ""; 
     string fil2 = ""; 
     string fil3 = ""; 
     string fil4 = ""; 

     // filter by time 
     switch (WithinTimeBtnStatus) 
     { 
      case WithinTime.All: 
       break; 
      case WithinTime.Hour: 
       string minusHour = (DateTime.Now - new TimeSpan(0, 1, 0, 0)).ToString("yyyy-MM-dd HH:mm:ss.fff"); 
       fil1 = $" timestamp >= datetime('{minusHour}')"; 
       break; 
      case WithinTime.Day: 
       string minusDay = (DateTime.Now - new TimeSpan(1, 0, 0, 0)).ToString("yyyy-MM-dd HH:mm:ss.fff"); 
       fil1 = $" timestamp >= datetime('{minusDay}')"; 
       break; 
      case WithinTime.Week: 
       string minusWeek = (DateTime.Now - new TimeSpan(7, 0, 0, 0)).ToString("yyyy-MM-dd HH:mm:ss.fff"); 
       fil1 = $" timestamp >= datetime('{minusWeek}')"; 
       break; 
     } 

     // filter by extension 
     for (int i = 0; i < FilteredExt.Count; i++) 
     { 
      fil2 += " ext != '" + FilteredExt[i] + "'"; 
      if (i < FilteredExt.Count - 1) 
       fil2 += " and"; 
     } 

     // filter by process 
     if (_processFilterSelected.ToLower() != "all" && _processFilterSelected != "") 
     { 
      fil3 = $" proc == '{_processFilterSelected}'"; 
     } 

     // filter by File Operation 
     if (_FileOperationFilterSelected.ToLower() != "all" && _FileOperationFilterSelected != "") 
     { 
      FileOperation fo = Converters.StringToFileOperation(_FileOperationFilterSelected); 
      switch (fo) 
      { 
       case FileOperation.Deleted: 
        fil4 = " oper == 'DELETED'"; 
        break; 
       case FileOperation.Renamed: 
        fil4 = " oper == 'RENAMED'"; 
        break; 
       case FileOperation.Modified: 
        fil4 = " oper == 'MODIFIED'"; 
        break; 
      } 
     } 


     string fil = ""; 
     var tmp = new[] { fil1, fil2, fil3, fil4 }; 
     foreach (var t in tmp) 
     { 
      if (t != "") 
      { 
       fil += " and" + t; 
      } 
     } 

     return fil; 
    } 
+0

嗯,至少你可以使用參數啓動。 – Fildor

+0

也許看看SqlCommandBuilder。 – Fildor

+0

我猜他想要使用LINQ。 –

回答

0

因爲我沒有得到滿意的答覆,我會寄我終於實現了。我認爲這可能是一個體面的方式,但可能有其他更好的方法來實現我所期待的(在我的數據庫中使用LINQ類型語法,而不是使用包含查詢的字符串)。

另外 - 我不確定這是更快,然後簡單地使用查詢字符串。

TL; DR:使用SQLite.CodeFirst + EntityFramework。 (在附註中,可能使用LINQ to SQL代替EntityFramework,但不確定是否也使用CodeFirst方法。一旦我嘗試並測試它,將會更新)。


第一件事情,你需要添加這些軟件包:

  • 實體框架
  • System.Data.SQLite(這可能也安裝:)
  • System.Data.SQLite。核心
  • System.Data.SQLite.EF6
  • System.Data.SQLite.Linq

最後,如果你從開始的代碼(像我一樣),你還需要

  • SQLite.CodeFirst

接下來要做的事情是建立在app.config中的連接字符串。 (在附註 - 似乎有很多錯誤指定提供程序,卸載和重新安裝上面的軟件包似乎修復。我不太確定刪除和添加背後的邏輯和不變的名稱 - 如果你關心我寫的,那就是:

<system.data> 
    <DbProviderFactories> 
     <remove invariant="System.Data.SQLite.EF6" /> 
     <add name="SQLite Data Provider (Entity Framework 6)" invariant="System.Data.SQLite.EF6" description=".NET Framework Data Provider for SQLite (Entity Framework 6)" type="System.Data.SQLite.EF6.SQLiteProviderFactory, System.Data.SQLite.EF6" /> 
     <remove invariant="System.Data.SQLite" /> 
     <add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".NET Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" /> 
    </DbProviderFactories> 
</system.data> 

<entityFramework> 
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" /> 
    <providers> 
     <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> 
     <provider invariantName="System.Data.SQLite.EF6" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" /> 
     <provider invariantName="System.Data.SQLite" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6"/> 
    </providers> 
    </entityFramework> 

連接字符串應指定一個名稱,至少到你的數據庫文件應位於的路徑。您也可以使用您稍後在代碼中定義的相對路徑(通過使用| DataDirectory目錄|語法):

<connectionStrings> 
    <add name="YourModel" connectionString="Data Source=|DataDirectory|\NameOfYourDBFile.sqlite" providerName="System.Data.SQLite" /> 
</connectionStrings> 

下一步,如果你正在做代碼首先,創建一個新的類,將是你的模型,這基本上是使用SQLite.CodeFirst包:

class YourModel : DbContext 
{ 
    // Your context has been configured to use a 'YourModel' connection string from your application's 
    // configuration file (App.config or Web.config). By default, this connection string targets the 
    // 'YourProject.YourModel' database on your LocalDb instance. 
    // 
    // If you wish to target a different database and/or database provider, modify the 'YourModel' 
    // connection string in the application configuration file. 
    public YourModel() 
     : base("name=YourModel") 
    { 
    } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     var sqliteConnectionInitializer = new SqliteCreateDatabaseIfNotExists<YourModel>(modelBuilder); 
     Database.SetInitializer(sqliteConnectionInitializer); 
     Database.SetInitializer(new SqliteDropCreateDatabaseWhenModelChanges<YourModel>(modelBuilder)); 
    } 

    // Add a DbSet for each entity type that you want to include in your model. For more information 
    // on configuring and using a Code First model, see http://go.microsoft.com/fwlink/?LinkId=390109. 

    public virtual DbSet<YourTableClass> YourTable { get; set; } 
} 

[Table("YourTable")] 
public class YourTableClass 
{ 
    [Key] 
    public string Id { get; set; } 
    [Required] 
    public FileOperation Oper { get; set; } 
    [Required, Index] 
    public OperationState State { get; set; } 
    [Index] 
    public string Proc { get; set; } 
    [Required] 
    public string Src { get; set; } 
    public DateTime Timestamp { get; set; } 
    // etc. 
} 

}

你可以閱讀更多關於它here

而這基本上是與準備。

(在一個側面說明,如果你想改變從後面的代碼數據庫文件的相對路徑,你需要寫:

AppDomain.CurrentDomain.SetData("DataDirectory", @"the\path\you\desire"); 

現在,你可以只使用它。基本語法很簡單,你只需使用:

using (var context = new YourModel()) 
{ 
    // some query 
} 

所以選擇

using (var context = new YourModel()) 
{ 
    var t = context.YourTable 
     .Where(e => e.State == OperationState.BackedUp) 
     .Select(e => e.Proc) 
     .Distinct() 
} 

如果你想插入

using (var context = new YourModel()) 
{ 
    var e = context.YourTable.Create(); 
    e.Id = guid; 
    // ...etc 
    e.Timestamp = timestamp; 
    context.YourTable.Add(e); 
    context.SaveChanges(); 
} 

如果你想仍使用字符串查詢:

using (var context = new YourModel()) 
{ 
    context.Database.ExecuteSqlCommand(comString); 
} 

一些重要的事情要記住:

  • 如果您在DB改變的東西,你必須調用Context.SaveChange()到底(你不需要這個了ExecuteSqlCommand)
  • 缺失是可以做到的RemoveRange + SaveChange()或仍然使用查詢字符串。

所以在這個問題GetFilterString更改例如:

public static IQueryable<YourTable> GetFilteredQueryable(IQueryable<YourTable> yourTable) 
    { 
     // filter by time 
     switch (RestoreLogic.WithinTimeBtnStatus) 
     { 
      case WithinTime.All: 
       break; 
      case WithinTime.Hour: 
       DateTime offsetHour = DateTime.Now.Add(new TimeSpan(-1, 0, 0)); 
       yourTable = yourTable.Where(e => e.Timestamp >= offsetHour); 
       break; 
      // etc. 
     } 

     // filter by extension 
     foreach (var i in FilteredExt) 
     { 
      yourTable = yourTable.Where(e => e.Ext != i); 
     } 

     // etc. 

     return yourTable; 
    } 
-2

編輯提供了一些答案。

本教程將向您介紹如何正確實現SQLite並使用Linq擴展與數據庫表進行交互。我已經複製下面的相關部分。一旦打開了數據庫連接並創建了數據第一個表,就可以像使用Linq的IEnumerable那樣與表進行交互。它還提供了將SQL作爲字符串傳遞的選項,但由於在編譯時未檢查此選項,因此存在運行時出錯的風險。

​​

的TodoItemDatabase構造如下:

public TodoItemDatabase(string dbPath) 
{ 
    database = new SQLiteAsyncConnection(dbPath); 
    database.CreateTableAsync<TodoItem>().Wait(); 
} 

這種方法創建保持打開,而在應用程序運行,從而避免開口的費用和關閉所述數據庫文件中的單個數據庫連接每次執行數據庫操作。 TodoItemDatabase類的其餘部分包含運行跨平臺的SQLite查詢。例如查詢代碼如下所示(對語法的更多細節可以在使用SQLite.NET文章中找到):

public Task<List<TodoItem>> GetItemsAsync() 
{ 
    return database.Table<TodoItem>().ToListAsync(); 
} 

public Task<List<TodoItem>> GetItemsNotDoneAsync() 
{ 
    return database.QueryAsync<TodoItem>("SELECT * FROM [TodoItem] WHERE [Done] = 0"); 
} 

public Task<TodoItem> GetItemAsync(int id) 
{ 
    return database.Table<TodoItem>().Where(i => i.ID == id).FirstOrDefaultAsync(); 
} 

public Task<int> SaveItemAsync(TodoItem item) 
{ 
    if (item.ID != 0) 
    { 
    return database.UpdateAsync(item); 
    } 
    else { 
    return database.InsertAsync(item); 
    } 
} 

public Task<int> DeleteItemAsync(TodoItem item) 
{ 
    return database.DeleteAsync(item); 
} 
+0

雖然這個鏈接可能回答這個問題,但最好在這裏包含答案的重要部分,並提供供參考的鏈接。如果鏈接頁面更改,則僅鏈接答案可能會失效。 - [來自評論](/ review/low-quality-posts/17239331) –

+0

我從該教程頁面複製了一些信息,因爲他有數據庫連接,其餘部分應該是微不足道的。 – Joagwa

+0

好吧,它是一個不同的包和類(沒有SQLiteAsyncConnection),但我想我可以在那裏發現,您可以簡單地在DB對象上使用常規的LINQ表達式。我會試着看看這是否有效。 –

相關問題