初学者的数据库最佳实践

所以,我是一个相当新的程序员,他的工作经验非常less,正在攻读大学生科学与技术学位。 在为我的程序寻找实习型工作时,我注意到我从几位教授那里听到的“使用数据库占现代计算机科学工作的90%” – 看起来确实如此。 然而,我的课程直到3年级才有真正的数据库课程,所以我至less要自己学习一些东西。

对于像我这样的人来说,我在networking和互联网上看到的东西很less。 似乎有大量关于如何读取和写入数据库中的数据的机制的教程,但关于相关的最佳实践很less。 为了说明我在说什么,并帮助解决我的实际问题,这里是在互联网上很容易find的东西 :

public static void Main () { using (var conn = new OdbcConnection()) { var command = new OdbcCommand(); command.Connection = conn; command.CommandText = "SELECT * FROM Customer WHERE id = 1"; var dbAdapter = new OdbcDataAdapter(); dbAdapter.SelectCommand = command; var results = new DataTable(); dbAdapter.Fill(results); } // then you would do something like string customerName = (string) results.Rows[0]["name"]; } 

等等。 这很容易理解,但显然充满了问题。 我从这样的代码开始,很快就开始说“像SQL这样的东西似乎很愚蠢,我应该把所有的东西放在一个常量文件中”。 然后我意识到,在这个地方有相同的代码行,并且把所有的连接对象等东西放在一个方法里面是很愚蠢的:

 public DataTable GetTableFromDB (string sql) { // code similar to first sample } string getCustomerSql = String.Format(Constants.SelectAllFromCustomer, customerId); DataTable customer = GetTableFromDB(getCustomerSql); string customerName = (string) customer.Rows[0]["name"]; 

这似乎是一个很大的改进。 现在,从OdbcConnection更改为SQLiteConnection非常容易。 但是,获取数据的最后一行依然显得尴尬。 并且改变一个字段名称(比如从“name”到“CustName”或其他东西)还是很痛苦的。 我开始阅读关于使用types化数据集或自定义业务对象 。 我仍然对所有的术语感到困惑,但决定无论如何都要仔细研究。 我认为,在我真正了解发生了什么以及为什么之前,依靠一个shiny的数据库向导为我做了所有这些工作(比如在链接的文章中)是愚蠢的。 所以我自己刺了一下,开始做这样的事情:

 public class Customer { public string Name {get; set;} public int Id {get; set;} public void Populate () { string getCustomerSql = String.Format(Constants.SelectAllFromCustomer, this.Id); DataTable customer = GetTableFromDB(getCustomerSql); this.Name = (string) customer.Rows[0]["name"]; } public static IEnumerable<Customer> GetAll() { foreach ( ... ) { // blah blah yield return customer; } } } 

以隐藏丑陋的表格东西,并提供一些强大的打字,允许外部代码只是做一些事情

 var customer = new Customer(custId); customer.Populate(); string customerName = customer.Name; 

这真的很好。 如果客户表更改,代码中的更改只需要在一个地方发生:在Customer类中。

所以,在所有这些散漫的结尾,我的问题是这样的。 我的数据库代码的缓慢发展是否朝着正确的方向发展? 我下一步去哪里? 这种风格对于小型数据库来说是非常好的,但是当有大量不同的表格时,为每一个表格写出所有这些类别将是一件痛苦的事情。 我听说过可以为你生成这种types代码的软件,但是DAL / ORM / LINQ2SQL /等术语仍然让人感到困惑,而那些巨大的软件也是令人难以置信的。 我正在寻找一些非常不错的复杂资源,可以指引我正确的方向。 我在这个主题上find的所有内容都是复杂的文章,或者只是向您展示如何使用Visual Studio中的“点击式”向导等的文章。 另外请注意,我正在寻找有关在代码中使用数据库的信息,而不是数据库devise/规范化的信息…有很多很好的材料。

感谢您阅读这个巨大的文字墙。

非常好的问题,你肯定是在正确的轨道上!

作为一名计算机工程师,数据库以及如何编写代码与数据库进行交互也不是我大学学位的重要组成部分,当然,我对所有数据库代码负责。

这是我的经验,使用从90年代初的遗留技术在一个项目和现代技术与C#和WPF在另一个。

我会尽我所能去解释术语,但我当然不是专家。

表,对象和映射哦,我的!

一个数据库包含表,但究竟是什么? 这只是与其他平面数据有关的平坦数据,如果你潜入并开始抓取事情,它将很快变得混乱! string将遍布各处,重复执行SQL语句,加载两次logging等等。因此,通常将每个表logging(或表logging的集合取决于其关系)表示为单个对象,通常称为作为一个模型。 这有助于封装数据并提供维护和更新其状态的function。

在您的发布您的客户类将作为模型! 所以你已经意识到了这个好处。

现在有各种各样的工具/框架(LINQ2SQL,dotConnect,Mindscape LightSpeed),可以为您编写所有的Model代码。 最后,他们映射对象到关系表或O / R映射引用它。

正如预期的那样,当你的数据库改变时,你的O / R映射也是如此。 就像你提到的那样,如果你的客户改变了,你必须把它修复到一个地方,为什么我们把东西放在课堂上。 在我的遗留项目中,更新模型耗费了大量的时间,因为它们太多了,而在我的新项目中只需点击几下,但最终结果是一样的。

谁应该知道什么?

在我的两个项目中,有两种不同的方式来表示对象如何与表格交互。

在一些阵营中,模型应该知道关于表格的所有信息,如何保存自己,直接共享访问连接/会话,并且可以自己执行像Customer.Delete()Customer.Save()这样的操作。

其他阵营,把阅读,写作,删除,pipe理类逻辑。 例如, MySessionManager.Save( myCustomer ) 。 这种方法的优点是能够轻松实现对对象的更改跟踪,并确保所有对象引用相同的基础表logging。 然而,实现它比以前提到的本地化类/表逻辑更复杂。

结论

你在正确的轨道上,我认为与数据库交互是非常有益的。 当我第一次开始研究自己的时候,我能记得我的脑袋在旋转。

我会build议尝试一下,开始一个小项目也许是一个简单的发票系统,并尝试自己写模型。 之后,尝试另一个小型项目,并尝试利用数据库O / R映射工具,看看不同之处。

你的演变绝对是正确的方向。 还有几件事要考虑:

  • 使用预处理语句与String.Format绑定参数。 这将保护你免受SQL注入攻击。
  • 使用DBProviderFactory和System.Data.Common接口进一步断开你的实现与特定的数据库。
  • 之后,查看一些方法来自动生成SQL命令并将数据映射到对象中。 如果你不想跳入一个复杂的大型ORM中,可以看一下简单的例子: 10分钟内的ADO.NET ORM , Light ORM库 ,或者在.NET中创build一个ORM 。 如果你决定走这条路线,最终你可以通过像Entity Framework , Hibernate或者SubSonic这样的成熟的库来更好的服务。

我的build议,如果你想了解数据库,第一步是忘记编程语言,接下来,忘记你正在使用哪个数据库和学习SQL。 当然,mySQL,MS SQLserver和Oracle之间有很多不同之处,但是还是有很多不同之处。

了解连接,select,date格式,规范化。 了解当数以百万计的logging和事情开始放缓时发生的事情,然后学会修复它。

创build一个与你感兴趣的东西相关的testing项目,例如一个自行车商店。 看看当你添加几百万个产品和几百万客户时会发生什么,并想到数据需要关联的所有方式。

使用桌面应用程序在本地数据库(sequel pro,mysql workbench等)上运行查询,因为它比将源代码上传到服务器要快得多。 和它一起玩吧!

恕我直言,你一定会朝着正确的方向努力维护代码! 不过,我不相信这种方法将扩展到一个真正的应用程序。 一些想法可能会有所帮助

  1. 虽然你正在编写的代码将非常好用,并且真正可以维护,但它涉及到大量的工作,这是巫师如此受欢迎的部分原因。 他们不是最好的工作,但要节省很多时间。
  2. 从数据库中查询仅仅是个开始, 一般来说,使用types化数据集和向导的另一个原因是在大多数应用程序中,用户在某个阶段将编辑您的信息并将其发送回来进行更新。 单个logging是好的,但是如果你的数据最好以规范化的方式表示,并且表4的层次更深,那么怎么办? 编写代码来自动生成更新/插入/删除语句,所有的调用都是地狱般的,所以工具是唯一的出路。 types的DataSets将会生成所有的代码来为你执行这些更新,并且具有一些非常强大的function来处理断开的(例如客户端)更新/最近修改的回滚。
  3. 最后一个家伙对SQL注入的看法 (这在行业中是一个严重的问题),通过使用DBCommand对象和添加DbParameters来保护自己。

总的来说,从代码到数据库(称为阻抗不匹配)是一个非常大的问题。 弥补差距是非常棘手的,这就是为什么大多数行业依靠工具来完成繁重的工作。 我的build议是尝试向导 – 因为在通过一个向导进行testing时,没有技巧的testing,学习所有的缺点/错误和各种解决方法是一个非常有用的技术在行业,并会让你得到一些更先进的数据pipe理中的场景更快(例如我提到的4深度表层次结构的断开更新)。

如果你对Linq to SQL和Entity Framework有些害怕,那么你可以跨进中间路线并探索像iBATIS.NET这样的东西。 它只是一个数据映射器工具,它可以承担数据库连接pipe理的一些痛苦,并将结果集映射到自定义域对象。

您仍然需要编写所有对象类和SQL,但是它会使用reflection将所有数据映射到您的类,而不必担心所有的基础连接(您可以轻松地编写一个工具生成你的类)。 当你用iBATIS启动并运行时(假设你可能感兴趣),你的代码将会看起来像这样:

 var customer = Helpers.Customers.SelectByCustomerID(1); 

SelectByCustomerID函数存在于Customers映射器中,其定义可能如下所示:

 public Customer SelectByCustomerID(int id) { Return Mapper.QueryForObject<Customer>("Customers.SelectByID", id); } 

“Customers.SelectByID”映射到XML语句定义,其中“Customers”是名称空间,“SelectByID”是包含SQL的映射的ID:

 <statements> <select id="SelectByID" parameterClass="int" resultClass="Customer"> SELECT * FROM Customers WHERE ID = #value# </select> </statements> 

或者当你想改变一个客户,你可以做的事情如:

 customer.FirstName = "George" customer.LastName = "Costanza" Helpers.Customers.Update(customer); 

LINQ to SQL和entity framework通过为你自动生成SQL而变得更加奇特。 我喜欢iBATIS,因为我仍然完全控制了SQL以及我的域对象的外观。

查看iBATIS (现在以MyBatis.NET名称迁移到Google)。 另一个很好的包是NHibernate ,它比iBATIS早了几步,更接近完整的ORM。

数据库的可视化页面只需要combobox和数据网格

命名空间

TestDatabase.Model

{class Database

 { private MySqlConnection connecting; private MySqlDataAdapter adapter; public Database() { connecting = new MySqlConnection("server=;uid=;pwd=;database=;"); connecting.Open(); } public DataTable GetTable(string tableName) { adapter = new MySqlDataAdapter("SELECT * FROM "+ tableName, connecting); DataSet ds = new DataSet(); adapter.Fill(ds); adapter.UpdateCommand = new MySqlCommandBuilder(adapter).GetUpdateCommand(); adapter.DeleteCommand = new MySqlCommandBuilder(adapter).GetDeleteCommand(); ds.Tables[0].RowChanged += new DataRowChangeEventHandler(Rowchanged); ds.Tables[0].RowDeleted += new DataRowChangeEventHandler(Rowchanged); return ds.Tables[0]; } public void Rowchanged(object sender, DataRowChangeEventArgs args) { adapter.Update(sender as DataTable); } } 

}

VMV数据库

 namespace TestDatabase.ViewModel { class MainViewModel : INotifyPropertyChanged { private Model.Database database; private DataTable table; public string[] options; public string selected; public DataTable Table { get { return table; } set { table = value; ChangeProperty("Table"); } } public string[] Options { get { return options; } } public string Selected { get { return selected; } set { selected = value; Table = database.GetTable(value); } } public MainViewModel() { database = new Model.Database(); options = new string[] { "" }; // names of tables } public event PropertyChangedEventHandler PropertyChanged; private void ChangeProperty(string name) { if (this.PropertyChanged != null) { this.PropertyChanged(this, new PropertyChangedEventArgs(name)); } } } }