2017-09-05 64 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表达式。我会试着看看这是否有效。 –