所以,我是一名相当新的程序员,正在攻读具有极少数工作经验的本科Comp Sci学位。在为我的课程寻找实习型工作时,我注意到我从几位教授那里听到的 - “使用数据库占所有现代计算机科学工作的90%” - 看起来确实如此。但是,我的课程直到第3年才开始有任何数据库课程,所以我试着至少自己学习一些东西。
对于像我这样的人,我在SO和互联网上看到的很少。似乎有大量关于如何在数据库中读取和写入数据的机制的教程,但很少有相关的最佳实践。为了演示我正在谈论的内容,并帮助我理解实际问题,这里有easily be found on the internet:
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是非常容易的。但访问数据的最后一行似乎仍然很尴尬;更改字段名称(例如从“名称”转到“CustName”或其他内容)仍然很痛苦。我开始阅读using typed Data sets or custom business objects。我仍然对所有术语感到困惑,但决定to look into it anyway.我认为依靠闪亮的数据库向导为我做所有这些事情(比如在链接的文章中)我真的学习之前是愚蠢的发生了什么,为什么。所以我自己就把它刺了一下,开始得到类似的东西:
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表发生更改,则代码中的更改只需要在一个位置进行:Customer
类内部。
所以,在所有这些漫无边际的结尾,我的问题是这个。我数据库代码的缓慢演变是否朝着正确的方向发展?我下一步去哪儿?这种风格对于小型数据库来说都很好,但是当有大量不同的表时,为每个表写出所有这些类都会很痛苦。我听说过可以为你生成这种类型的代码的软件,但是仍然有点被DAL / ORM / LINQ2SQL / etc行话弄糊涂了,那些巨大的软件都是压倒性的。我正在寻找一些优秀而非压倒性复杂的资源,可以指引我朝着正确的方向前进。我在这个主题上找到的只是复杂的文章,或者只是向您展示如何在Visual Studio中使用点击式向导的文章。另请注意,我正在寻找有关在代码中使用数据库的信息,而不是有关数据库设计/规范化的信息...那里有很多很好的材料。
感谢您阅读这一巨大的文字墙。
答案 0 :(得分:18)
确实非常好的问题,你肯定是在正确的轨道上!
作为一名计算机工程师,数据库以及如何编写与数据库交互的代码也不是我大学学位的重要组成部分,而且我确实负责工作中的所有数据库代码。
这是我的经验,使用90年代早期的传统技术进行一个项目,使用C#和WPF进行现代技术。
我会尽我所能解释术语,但我自己肯定不是专家。
表格,对象和映射哦我的!
数据库包含表但实际上是什么?这只是与其他平面数据相关的平面数据,如果你潜入并开始抓住它会迅速变得混乱!字符串将遍布各处,SQL语句重复,记录加载两次等等...因此,将每个表记录(或表记录集合取决于它们的关系)表示为单个对象通常是一种很好的做法,通常称为作为一个模型。这有助于封装数据并提供维护和更新其状态的功能。
在您的发布中,您的Customer类将充当模型!所以你已经意识到这种好处。
现在有各种各样的工具/框架(LINQ2SQL,dotConnect,Mindscape LightSpeed)可以为您编写所有的Model代码。最后,他们将对象映射到关系表或O / R映射,因为它们引用它。
正如您的数据库更改所预期的那样,您的O / R映射也是如此。就像你提到的那样,如果你的客户发生变化,你必须在一个地方修复它,这也是为什么我们把东西放在课堂上。对于我的遗留项目,更新模型消耗了大量时间,因为它们太多了,而在我的新项目中只需点击几下,但最终结果是相同的。
谁应该知道什么?
在我的两个项目中,有两种不同的方式来处理对象与表的交互方式。
在某些阵营中,模型应该知道有关其表格的所有内容,如何自我保存,对连接/会话具有直接共享访问权限,并且可以自行执行Customer.Delete()
和Customer.Save()
等操作。
其他阵营,在管理课上放读,写,删除,逻辑。例如, MySessionManager.Save( myCustomer )
。该方法的优点是能够轻松实现对象的更改跟踪并确保所有对象引用相同的基础表记录。然而,实现它比前面提到的本地化类/表逻辑方法更复杂。
<强>结论强>
你走在正确的轨道上,在我看来,与数据库交互是非常有益的。当我第一次开始自己研究时,我记得我的脑袋旋转。
我建议尝试一下,启动一个小项目可能是一个简单的发票系统,并尝试自己编写模型。之后尝试另一个小项目并尝试利用数据库O / R映射工具并查看差异。
答案 1 :(得分:8)
你的进化肯定是正确的方向。还有一些事情需要考虑:
答案 2 :(得分:3)
我的建议如果你想了解数据库,第一步是忘记编程语言,接下来,忘记你正在使用哪个数据库并学习SQL。当然,mySQL,MS SQLserver和Oracle之间存在很多差异,但有很多差异是相同的。
了解联接,选择日期格式,规范化。了解当您拥有数百万条记录并且事情开始变慢时会发生什么,然后学会修复它。
创建与您感兴趣的内容相关的测试项目,例如自行车商店。看看当你添加几百万个产品和几百万客户时会发生什么,并想到数据需要的所有相关方式。
使用桌面应用程序在本地数据库(续集专业版,mysql工作台等)上运行查询,因为它比将源代码上传到服务器要快得多。并享受它的乐趣!
答案 3 :(得分:2)
恕我直言,你肯定会朝着正确的方向努力,以便能够使用可维护的代码!但是我不相信这种方法可以扩展到真正的应用程序。一些可能有用的想法
一般来说,从代码到数据库(称为impedance mismatch)存在一个非常大的问题。弥合差距是非常棘手的,这就是为什么大多数行业依赖工具来完成繁重的工作。我的建议是尝试向导 - 因为虽然单步执行向导不是技能测试,但学习所有缺点/错误及其各种变通方法是行业中非常有用的技能,并且可以让您获得更高级的技能数据管理中的场景更快(例如我提到的4深表层次结构的断开连接更新)。
答案 4 :(得分:2)
如果你有点害怕像Linq to SQL和Entity Framework这样的东西,你可以介入一半,探索像iBATIS.NET这样的东西。它只是一个数据映射器工具,可以解决数据库连接管理的一些难题,并将结果集映射到自定义域对象。
您仍然需要编写所有对象类和SQL,但它会使用反射将所有数据映射到类中,并且您不必担心所有底层连接(您可以轻松编写)一个生成类的工具)。当您启动并运行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和实体框架通过自动生成SQL来获得更好的体验。我喜欢iBATIS,因为我仍然可以完全控制SQL以及我的域对象的样子。
Check out iBATIS(现已以MyBatis.NET名称迁移到Google)。另一个很棒的包是NHibernate,它比iBATIS提前几步,更接近完整的ORM。
答案 5 :(得分:-2)
使用combobox和datagrid
的数据库的可视化页面命名空间
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);
}
}
}