设计.NET应用程序数据访问层五大原则

程序|访问|设计|数据

  摘要:大多数使用.NET框架组件工作的开发人员的一个核心工作是实现数据访问功能,他们建立的数据访问层(data access layer)是应用程序的精华部分。本文概述了使用Visual Studio .NET和.NET框架组件建立数据访问层需要考虑的五个想法。这些技巧包括通过使用基类(base class)利用面相对象技术和.NET框架组件基础结构,使类容易继承,在决定显示方法和外部界面前仔细地检验需求。

  如果你正在建立以数据为中心(data-centric)的.NET框架组件应用程序,你最终必须建立数据访问层。也许你知道在.NET框架组件中建立自己的代码有很多好处。因为它支持实现和接口(interface)继承,你的代码更容易重复使用,特别是被使用不同的框架组件兼容(Framework-compliant)语言的开发人员使用。本文我将概述为基于.NET框架组件的应用程序建立数据访问层的五条规则。

  开始前,我必须提醒你建立的任何基于本文讨论的规则的数据访问层必须与传统Windows平台上开发人员喜欢的多层或者n层应用程序兼容。在这种结构中,表现层包含Web窗体、Windows窗体、调用与数据访问层的工作相应的事务层的XML服务代码。该层由多个数据访问类(data access classe)组成。换句话说,在事务处理协调不是必要的情况下,表现层将直接调用数据访问层。这种结构是传统的模型-视列表-控制程序(Model-View-Controller,MVC)模式的变体,在多种情况下被Visual Studio .NET和它暴露的控件采用。

  规则1:使用面向对象特性

  最基本的面向对象事务是建立一个使用实现继承的抽象类。这个基类可以包括你的所有数据访问类通过继承能够使用的服务。如果那些服务足够了,它们就能通过在整个组织的基类分布实现重复使用。例如最简单的情况是基类能够为衍生类处理连接的建立过程,如列表1所示。

Imports System.Data.SqlClient

Namespace ACME.Data
Public MustInherit Class DALBase : Implements IDisposable
Private _connection As SqlConnection

Protected Sub New(ByVal connect As String)
_connection = New SqlConnection(connect)
End Sub

Protected ReadOnly Property Connection() As SqlConnection
Get
Return _connection
End Get
End Property

Public Sub Dispose() Implements IDisposable.Dispose
_connection.Dispose()
End Sub

End Class
End Namespace

  列表1.简单基类

  在列表中可以看到,对DALBase类作了MustInherit标记(C#中的抽象),以确保它在继承关系中使用。接着该类在公共构造函数中包括了一个实例化的私有SqlConnection对象,它接收连接字符串作为一个参数。当来自IDisposable接口的Dispose方法确保连接对象已经被配置了的时候,受保护的(protected)Connection属性允许衍生类访问该连接对象。

  即使在下面简化的例子中你也能开始看到抽象基类的用处:

Public Class WebData : Inherits DALBase
Public Sub New()
MyBase.New(ConfigurationSettings.AppSettings("ConnectString"))
End Sub

Public Function GetOrders() As DataSet
Dim da As New SqlDataAdapter("usp_GetOrders", Me.Connection)
da.SelectCommand.CommandType = CommandType.StoredProcedure
Dim ds As New DataSet()

da.Fill(ds)
Return ds
End Function
End Class

  在这种情况下,WebData类继承自DALBase,结果就是不必担心实例化SqlConnection对象,而是通过MyBase关键字(或者C#中的基关键字)简单地把连接字符串传递给基类。WebData类的GetOrders方法能使用Me.Connection(在C#中是this.Connection)访问受保护的属性。虽然这个例子相对简单,但是你将在规则2和3中看到基类也提供了其它的服务。

  当数据访问层必须在COM+环境中运行时抽象的基类很有用。在这种情况下,因为允许组件使用COM+的必要代码复杂得多,所以更好的方式是建立一个如列表2所示的服务组件(serviced component)基类。

Transaction(TransactionOption.Supported), _
EventTrackingEnabled(True)> _
Public MustInherit Class DALServicedBase : Inherits ServicedComponent

Private _connection As SqlConnection

Protected Overrides Sub Construct(ByVal s As String)
_connection = New SqlConnection(s)
End Sub

Protected ReadOnly Property Connection() As SqlConnection
Get
Return _connection
End Get
End Property
End Class

  列表2.服务组件基类

  在这段代码中,DALServicedBase类包含的基本功能与列表1中的相同,但是加上了从System.EnterpriseServices名字空间的ServicedComponent的继承,并且包括了一些属性,指明组件支持对象构造、事务和静态跟踪。接着该基类仔细地捕捉组件服务管理器(Component Services Manager)中的构造字符串并且再次建立和暴露SqlConnection对象。我们要注意的是当一个类继承自DALServicedBase时,它也继承了属性的设置。换句话说,一个衍生类的事务选项也设置为Supported。如果衍生类想重载这种行为,它能在类的层次重新定义该属性。

  此外,衍生类在适当情况下应该有利于自身重载和共享方法。使用重载的方法(一个方法有多个调用信号)在本质上有两种情况。首先,它们在一个方法需要接受多种类型的参数时使用。框架组件中的典型例子是System.Convert类的方法。例如ToString方法包含18个接受一个参数的重载方法,每个重载方法的类型不同。其次,重载的方法用于暴露参数数量不断增长的信号,而不是不同类型的必要参数。在数据访问层中这类重载变得效率很高,因为它能用于为数据检索和修改暴露交替的信号。例如GetOrders方法可以重载,这样一个信号不接受参数并返回所有订单,但是附加的信号接受参数以表明调用程序希望检索特定的顾客订单,代码如下:

Public Overloads Function GetOrders() As DataSet
Public Overloads Function GetOrders(ByVal customerId As Integer) As DataSet

  这种情况下的一个好的实现技巧是抽象GetOrders方法的功能到一个能被每个重载信号调用的私有的或者受保护的方法中。

  共享方法(C#中的静态方法)也能用于暴露数据访问类的所有实例能够访问的字段、属性和方法。尽管共享成员不能与使用组件服务(Component Services)的类一起使用,但是对于在数据访问类的共享构造函数中检索并被所有实例读取的只读数据是有用的。使用共享成员读/写数据时要小心,因为为了访问该共享数据,执行的多个线程可能会竞争。

  规则2:坚持设计指导

  随Visual Studio .NET一起发布的在线文档中有一个叫"类库开发人员的设计指导(Design Guidelines for Class Library Developers)"的主题,它覆盖了类、属性和方法的名字转换,是重载的成员、构造函数和事件的补充模式。你必须遵循名字转换的主要原因之一是.NET框架组件提供的跨语言(cross-language)继承。如果你在Visual Basic .NET中建立一个数据访问层基类,你想确保使用.NET框架组件兼容的其它语言的开发人员能继承它并容易理解它怎样工作。通过坚持我概述的指导方针,你的名字转换和构造就不会是语言特定的(language specific)。例如,你可能注意到在本文例子的代码中第一个词小写,并加上intercaps是用于方法的参数的,每个词大写是用于方法的,基类使用Base标志来标识它是一个抽象类。

  可以推测.NET框架组件设计指导都是普通设计模式,像Gang of Four (Addison-Wesley, 1995)写的Design Patterns记载的一样。例如.NET框架组件使用了Observer模式的一个变体,叫做Event模式,在类中暴露事件时你必须遵循它。

  规则3:利用基础结构(Infrastructure)

  .NET框架组件包括一些类和构造,它们能辅助处理通常的与基础结构相关的事务,例如装置和异常处理。通过基类把这些概念与继承组合起来将非常强大。例如,你能考虑一下System.Diagnostics名字空间中暴露的跟踪功能。除了提供Trace和Debug类外,该名字空间还包括衍生自Switch和TraceListener的类。Switch类的BooleanSwitch和TraceSwitch能被配置用于打开和关闭应用程序和配置文件,在TraceSwitch中可以暴露多层次跟踪。TraceListener类的TextWriterTraceListener和EventLogTraceListener分别将Trace和Debug方法的输入定位到文本文件和事件日志。

  这样作的结果是给基类添加了跟踪功能,使衍生类记录消息日志更简单。接着应用程序能使用配置文件控制是否允许跟踪。你能包括一个BooleanSwitch类型的私有变量并在构造函数中实例化它来给列表1中的DALBase添加这个功能:

Public Sub New(ByVal connect As String)
_connection = New SqlConnection(connect)
_dalSwitch = New BooleanSwitch("DAL", "Data Access Code")
End Sub

  传递给BooleanSwitch的参数包括名字和描述。接着你能添加一个受保护的属性打开和关闭开关,也能添加一个属性使用Trace对象的WriteLineIf方法格式化并写入跟踪消息:

Protected Property TracingEnabled() As Boolean
Get
Return _dalSwitch.Enabled
End Get
Set(ByVal Value As Boolean)
_dalSwitch.Enabled = Value
End Set
End Property

Protected Sub WriteTrace(ByVal message As String)
Trace.WriteLineIf(Me.TracingEnabled, Now & ": " & message)
End Sub

  通过这种途径,衍生类自己并不知道开关(switch)和监听(listener)类,当数据访问类产生一个有意义的信号时能够简单地调用WriteTrace方法。

type="System.Diagnostics.TextWriterTraceListener"
initializeData="DALLog.txt" />

  列表3.跟踪的配置文件

  为了建立一个监听器并打开它,需要使用应用程序配置文件。列表3显示了一个简单的配置文件,它能够打开刚才显示的数据访问类开关,并通过myListener调用TextWriterTraceListener把输出定位到文件DALLog.txt中。当然,你能通过从TraceListener类衍生程序化地建立监听器并把该监听器直接包含在数据访问类中。

Public Class DALException : Inherits ApplicationException
Public Sub New()
MyBase.New()
End Sub

Public Sub New(ByVal message As String)
MyBase.New(message)
End Sub

Public Sub New(ByVal message As String, ByVal innerException As
Exception)
MyBase.New(message, innerException)
End Sub
'在这儿添加自定义成员
Public ConnectString As String
End Class

  列表4.自定义异常类

  你从中收益的第二个基础结构是结构化异常处理(SEH)。在最基本的层次,数据访问类能够暴露它的衍生自System.ApplicationException 的Exception(异常)对象并能进一步暴露自定义成员。例如,列表4中显示的DALException对象能用于包装数据访问类中的代码产生的异常。接着基类能暴露一个受保护的方法包装该异常,组装自定义成员,并把它发回给调用程序,如下所示:

Protected Sub ThrowDALException(ByVal message As String, _
ByVal innerException As Exception)
Dim newMine As New DALException(message, innerException)

newMine.ConnectString = Me.Connection.ConnectionString
Me.WriteTrace(message & "{" & innerException.Message & "}")
Throw newMine
End Sub

  使用这种方法,衍生类能简单地调用受保护的方法,传递进去一个特定的数据异常(典型的有SqlException或者 OleDbException),该异常被截取并添加了从属于特定数据域的消息。基类在DALException中包装该异常并把它发回到调用程序。这就允许调用程序用一个Catch语句轻易地捕捉所有来自数据访问类的异常。

  作为选择之一,你可以看一看MSDN上发布的"Exception Management Application Block Overview"。该框架组件通过一系列对象结合了异常和应用程序日志记录。实际上,通过从.NET 框架组件提供的BaseApplicationException类衍生的自定义异常类能够简单地插入该框架组件。

  规则4:仔细选择外部界面

  在你设计数据访问类的方法时,需要考虑它们怎样接受和返回数据。对大多数开发人员来说,主要有三个选择:直接使用ADO.NET对象、使用XML、使用自定义类。

  如果直接暴露ADO.NET对象,你能使用一到两个编程模型。第一个包括数据集和数据表对象,它们对不连接数据访问很有用。有很多关于数据集和与它关联的数据表的文章,但是当你必须使用从下层数据存储断开的数据时它才最有用处。换句话说,数据集能在应用程序各层之间传递,即使那些层在物理上是分布式的,当业务和数据服务层放置在同一群服务器上并且与表现服务分开时也能使用。此外,数据集对象是通过基于XML的Web服务返回数据的理想方法,因为它们是可串行化的,因此能在SOAP回应消息中返回。

  这与使用实现IDataReader接口的类(例如SqlDataReader 和OleDbDataReader)访问数据不同。数据阅读器(data reader)用只向前的,只读的方式访问数据。两者之间最大的不同是数据集和数据表对象能在应用程序域之间传递,通过传递值(by value)实现,然而数据阅读器能在各处传递,但是一般通过引用(by reference)实现。在列表5中,Read和GetValues在服务器过程中执行并且它们的返回值复制到客户端。

  该图显示了数据阅读器怎样存活在应用程序域中,它在那儿它被建立,并且对它的所有访问结果都在客户端和服务器应用程序域之间的循环之中。这意味着当数据访问方法在相同的应用程序域运行时,应该返回数据阅读器作为调用者。

  使用数据阅读器时有两个问题需要考虑。首先,当你从数据访问类的一个方法返回数据阅读器时,你必须考虑与数据阅读器关联的连接对象的生存期。默认情况是当调用程序通过数据阅读器重复时连接仍然是忙的,不幸的是当调用程序结束后,连接仍然打开,因此它不返回到连接池(如果允许连接池)。但是,当通过传递CommandBehavior.CloseConnection 枚举给command对象的ExecuteReader方法,连接的Close方法被调用时,你能命令数据阅读器关闭它的连接。

  其次,为了把表现层从特定的框架组件数据提供程序(例如SqlClient或者OleDb)中分离出来,调用代码应该使用IDataReader接口(例如SqlDataReader)而不是具体类型来引用返回值。通过这种方法,如果应用程序后端从Oracle移植到 SQL Server,或者数据访问类的一个方法的返回类型改变了,表现层也不需要更改。

  如果你希望数据访问类返回XML,你可以从System.Xml名字空间中的XmlDocument和XmlReader中选择一个,它与数据集和IDataReader类似。换句话说,当数据从数据源断开时你的方法应该返回一个XmlDocument(或者XmlDataDocument),然而XmlReader可用于访问XML数据的流。

  最后,你也能决定与公共属性一起返回自定义类。这些类可以使用Serialization(串行化)属性标记,这样它们就能跨越应用程序域复制。另外,如果你从方法中返回多个对象,就需要强化类型(strongly typed)的集合类。

Imports System.Xml.Serialization

_
Public Class Book : Implements IComparable
Public ProductID As Integer
Public ISBN As String
Public Title As String
Public Author As String
Public UnitCost As Decimal
Public Description As String
Public PubDate As Date

Public Function CompareTo(ByVal o As Object) As Integer _
Implements IComparable.CompareTo
Dim b As Book = CType(o, Book)
Return Me.Title.CompareTo(b.Title)
End Function
End Class

Public NotInheritable Class BookCollection : Inherits ArrayList
Default Public Shadows Property Item(ByVal productId As Integer) _
As Book
Get
Return Me(IndexOf(productId))
End Get
Set(ByVal Value As Book)
Me(IndexOf(productId)) = Value
End Set
End Property

Public Overloads Function Contains(ByVal productId As Integer) As _
Boolean
Return (-1 <> IndexOf(productId))
End Function

Public Overloads Function IndexOf(ByVal productId As Integer) As _
Integer
Dim index As Integer = 0
Dim item As Book

For Each item In Me
If item.ProductID = productId Then
Return index
End If
index = index + 1
Next
Return -1
End Function

Public Overloads Sub RemoveAt(ByVal productId As Integer)
RemoveAt(IndexOf(productId))
End Sub

Public Shadows Function Add(ByVal value As Book) As Integer
Return MyBase.Add(value)
End Function
End Class

  列表6.使用自定义类

  上列表(列表6)包含了一个简单的Book类和与它关联的集合类的例子。你能注意到Book类用Serializable做了标记,使它跨越应用程序域能使用"by value"语法。该类实现了IComparable接口,因此当它包含在一个集合类中的时候,默认情况下它将按Title排序。BookCollection类从System.Collections名字空间的ArrayList衍生,并且为了将该集合限制到Book对象而隐藏了Item属性和ADD方法。

  通过使用自定义类你完全地控制了数据的表现、开发人员的效率并且没有依赖ADO.NET的调用。但是这种途径需要更多的代码,因为.NET框架组件没有包含任何与对象相关的技术映射。在这种情况下,你应该在数据访问类中建立一个数据读取器并使用它来组合自定义类。

  规则5:抽象.NET框架组件数据提供程序

  最后一条规则说明了为什么和怎样抽象数据访问类内部使用的.NET框架组件数据提供程序(data provider)。先前我说过ADO.NET编程模型暴露了特定的.NET框架组件数据提供程序,包括SqlClient、OleDb和其它MSDN Online Web站点上可用的。但是这种设计的结果是提高性能,为数据提供程序暴露特定数据源功能的能力,它强迫你决定使用那种数据提供程序编码。换句话说,开发人员典型地会选择使用SqlClient或OleDb,接着在各自的名字空间直接对它们的类进行编程。

  如果你想改变.NET框架组件数据提供程序,你必须重新编写数据访问方法。为了避免这种情况发生,你可以使用Abstract Factory设计模式。使用这种模式,你能建立一个简单的类,它暴露方法来建立主要的.NET框架组件数据提供程序对象(command、connection、data adapter和parameter),而那些对象基于传递给构造函数的.NET框架组件数据提供程序的信息。列表7中的代码就是这样一个简单的类。

public enum ProviderType :int {SqlClient = 0, OLEDB = 1}

public class ProviderFactory {
public ProviderFactory(ProviderType provider) {
_pType = provider;
_initClass();
}

public ProviderFactory() {
_initClass();
}

private ProviderType _pType = ProviderType.SqlClient;
private bool _pTypeSet = false;
private Type[] _conType, _comType, _parmType, _daType;

private void _initClass() {
_conType = new Type[2];
_comType = new Type[2];
_parmType = new Type[2];
_daType = new Type[2];

// 为提供程序初始化类型
_conType[(int)ProviderType.SqlClient] = typeof(SqlConnection);
_conType[(int)ProviderType.OLEDB] = typeof(OleDbConnection);
_comType[(int)ProviderType.SqlClient] = typeof(SqlCommand);
_comType[(int)ProviderType.OLEDB] = typeof(OleDbCommand);
_parmType[(int)ProviderType.SqlClient] = typeof(SqlParameter);
_parmType[(int)ProviderType.OLEDB] = typeof(OleDbParameter);
_daType[(int)ProviderType.SqlClient] = typeof(SqlDataAdapter);
_daType[(int)ProviderType.OLEDB] = typeof(OleDbDataAdapter);
}

public ProviderType Provider {
get {
return _pType;
}
set {
if (_pTypeSet) {
throw new ReadOnlyException("Provider already set to "
+ _pType.ToString());
}
else {
_pType = value;
_pTypeSet = true;
}
}
}
public IDataAdapter CreateDataAdapter(string commandText,IDbConnection
connection) {
IDataAdapter d;
IDbDataAdapter da;

d = (IDataAdapter)Activator.CreateInstance(_daType[(int)_pType],
false);
da = (IDbDataAdapter)d;
da.SelectCommand = this.CreateCommand(commandText, connection);
return d; }

public IDataParameter CreateParameter(string paramName, DbType
paramType) {
IDataParameter p;
p = (IDataParameter)Activator.CreateInstance(_parmType[(int)_pType],
false);
p.ParameterName = paramName;
p.DbType = paramType;
return p;
}

public IDataParameter CreateParameter(string paramName, DbType
paramType, Object value) {
IDataParameter p;
p = (IDataParameter)Activator.CreateInstance(_parmType[(int)_pType],
false);
p.ParameterName = paramName;
p.DbType = paramType;
p.Value = value;
return p;
}

public IDbConnection CreateConnection(string connect) {
IDbConnection c;
c = (IDbConnection)Activator.CreateInstance(_conType[(int)_pType],
false);
c.ConnectionString = connect;
return c;
}

public IDbCommand CreateCommand(string cmdText, IDbConnection
connection) {
IDbCommand c;
c = (IDbCommand)Activator.CreateInstance(_comType[(int)_pType],
false);
c.CommandText = cmdText;
c.Connection = connection;
return c;
}
}

  列表7. ProviderFactory

  为了使用该类,数据访问类的代码必须对多个.NET框架组件数据提供程序实现的接口(包括IDbCommand、IDbConnection、IDataAdapter和IDataParameter)进行编程。例如,为了使用一个参数化存储过程的返回值来填充数据集,必须在数据访问类的某个方法中有下面的代码:

Dim _pf As New ProviderFactory(ProviderType.SqlClient)
Dim cn As IDbConnection = _pf.CreateConnection(_connect)
Dim da As IDataAdapter = _pf.CreateDataAdapter("usp_GetBook", cn)

Dim db As IDbDataAdapter = CType(da, IDbDataAdapter)
db.SelectCommand.CommandType = CommandType.StoredProcedure
db.SelectCommand.Parameters.Add(_pf.CreateParameter("@productId",DbType.Int32, id))

Dim ds As New DataSet("Books")
da.Fill(ds)

  典型的情况是你在类的层次声明ProviderFactory变量并在数据访问类的构造函数中实例化它。另外,它的构造函数与从配置文件中读取的提供程序一起组装,而不应该是硬代码。你可以想象,ProviderFactory是数据访问类的一个重大的补充,并且能被包括进部件,分发给其它的开发人员。

  结论

  在Web服务时代将建立越来越多的应用程序操作来自独立的应用程序层的数据。如果你遵循一些基本规则并形成习惯,编写数据访问代码将更快、更容易,并且更能重新使用,把你的错误保存到服务器,允许你保持数据独立。

时间: 2024-10-31 10:56:37

设计.NET应用程序数据访问层五大原则的相关文章

《解剖PetShop》系列之二:PetShop数据访问层之数据库访问设计

在系列一中,我从整体上分析了PetShop的架构设计,并提及了分层的概念.从本部分开始,我将依次对各层进行代码级的分析,以求获得更加细致而深入的理解.在PetShop 4.0中,由于引入了ASP.Net 2.0的一些新特色,所以数据层的内容也更加的广泛和复杂,包括:数据库访问.Messaging.MemberShip.Profile四部分.在系列二中,我将介绍有关数据库访问的设计. 在PetShop中,系统需要处理的数据库对象分为两类:一是数据实体,对应数据库中相应的数据表.它们没有行为,仅用于

其实添加数据也可以这样简单——表单的第一步抽象(针对数据访问层)《怪怪设计论: 抽象无处不在 》有感

更正:不好意思,昨天晚上思路有点混乱.有几个前提忘记说明了,现在补充一下. 1.缩小范围.按照由简到难的思路,这里先讨论最简单的添加数据的情况.就是单表的添加和修改:这里讨论的是webform的情况. 2.第一步抽象是针对数据访问层的抽象. 如果我没有理解错的话,现在大多数人的做法是:有一个表(或者几个有关联的表)在数据层里就要有一个"函数"与之对应, 如果采用的是SQL语句的方式的话,那么函数的内筒就是组合SQL语句的代码, 如果采用的是存储过程的方式的话,那么函数的内筒就是给存储过

统一身份认证子系统数据库设计与数据访问层实现

访问|设计|数据|数据库|数据库设计 目 录 一 引言--------------------------------1 二 需求分析 (一)系统的功能要求------------------------2 (二)系统的性能要求------------------------2 (三)运行环境要求-------------------------2 (四)开发工具简介-------------------------2 三 总体设计 (一)系统模块化分----------------------

Winform开发框架之数据访问层的设计

我在前面几篇介绍过我的Winform开发框架的相关内容,其中主要集中在界面展现以及各层的总体设计上,还没有空来得及深入进行各个重要细节的讲解,今天我们来介绍.讨论下我的Winform开发框架之数据访问层的设计方面的知识,希望对大家有所帮助. 前面介绍过,该Winform开发框架支持多种数据库的访问,只需要对配置进行修改即可切换,如下图所示.   其中我们可以看到,里面有几种数据访问层的相关数据,大致知道是通过同一的IDAL数据访问层接口派生出几个对应不同数据库的操作层,由业务逻辑层BLL进行相应

ASP.NET2.0数据操作之创建数据访问层(1)

asp.net|创建|访问|数据 导言 作为web开发人员,我们的生活围绕着数据操作.我们建立数据库来存储数据,写编码来访问和修改数据,设计网页来采集和汇总数据.本文是研究在ASP.NET 2.0中实现这些常见的数据访问模式之技术的长篇系列教程的第一篇.我们将从创建一个软件框架开始,这个框架的组成部分包括一个使用强类型的DataSet的数据访问层(DAL),一个实施用户定义的业务规则的业务逻辑层(BLL),以及一个由共享页面布局的ASP.NET网页组成的表现层.在打下这个后端的基础工作之后,我们

ASP.NET2.0数据操作之创建数据访问层(4)

asp.net|创建|访问|数据 第五步:完成数据访问层 注意,ProductsTableAdapters类从Products表中返回的 是CategoryID和SupplierID的值,但并不包括Categories表 的CategoryName字段和Suppliers表的CompanyName字段,尽管当 我们显示产品信息时,这些很可能是我们想要显示的字段.我们可以扩充TableAdapter的起始方 法GetProducts()来包含CategoryName和CompanyName字段的值

剖析 .Net 下的数据访问层技术(一)

访问|数据 l 引言 自从 .NET 真正走入开发人员那天起,"效率"两个字就一直成为众多程序员津津乐道的话题.无论是从开发模式(Cross Language).系统框架(.NET Framework),还是各种使用方便的工具(VS.NET),无一不体现出了它的胜人一筹. 同时,在另一方面,.NET 是否可以真正胜任企业级应用(Enterprise Application)开发的重任,却依然争论不断,褒贬不一. 通常来说,对于一个企业级应用,需要考虑的方面很多,如安全.性能.伸缩性.易

编写可移植数据访问层

访问|数据 摘要:了解如何编写透明地使用不同数据源(从 Microsoft Access 到 SQL Server 以及 Oracle RDBMS)的智能应用程序. 本页内容 引言 使用通用数据访问方法 使用基本接口 编写专门的数据访问层 从其他层使用数据访问类 一些可能的改进 结论 引言在负责咨询工作的过去 6 年中,我曾多次听说关于数据访问和操作方面的问题,它时刻困扰着用户:"如何编写应用程序,以便只需对其进行很少的改动或不进行改动即可使用数据库服务器 x.y 和 z?"由于知道数

基于.NET平台的分层架构实战(九)—数据访问层的第三种实现:基于NBear框架

基于.NET平台的分层架构实战(九)-数据访问层的第三种实现:基于NBear框架的ORM实现 前面的文章讨论了使用SQL语句和存储过程两种数据访问层的实现方式,这一篇里,将讨论使用ORM方式实现数据访问层的方法. 对象-关系映射(Object/Relation Mapping,简称ORM),是随着面向对象的软件开发方法发展而产生的.面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统.对象和关系数据是业务实体的两种表现形式,业务实