我做的:1、DataAccess,跨数据访问,专门用于数据存取,已封装
IDataAccess 数据库打开关闭、执行SQL等
IDataAccessFactory 工厂,根据配置文件实例多种数据访问,已实现Access、SQLServer、Oracle、DB2、OleDB、ODBC2、EntityAccess,跨数据访问,调用数据访问层,为了与DataAccess实现松耦合,增加了IExeSql接口,因而只要你的数据访问实现了IExeSql接口也行,已封装 IEntityAccess,依赖IEntityFacade,调用IExeSql,是IEntityMap与DB的中间产物,已封装
IEntityMap,依赖IEntityFacade,调用IEntityAccess的增、删、改查
IEntityFacade,不用说了,仅仅是对象,这样不像强类型暴露了数据结构这里的命名刚更改为现在这个名,正好和数据访问、中间层、表现层对应3、实体定义与实体管理,实现IEntityFacade、IEntityMap接口或EntityAccess给定的基类
EntityDefine如PersonEntiy
例如成员Name,Age,这个名字不一定是数据库中Person表的字段名,这样很好的保护了结构不暴露,UI调用也方便,直接调用person.Name比强类型的dataRow["Name"].ToString()更直接方便,如果用强类型,UI还要了解结构,不爽。
如果你不会在意像DataSet强类型暴露数据结构,EntityManagement直接继承IEntityFacade的已实现的基本类EntityFacade并实现IEntityMap EntityManagement如PersonManagement
调用者自己用成员适配模式定义EntityManagement适配IEntityMap的增删改查,这样就不会暴露数据结构
如果你不会在意像DataSet强类型暴露数据结构,EntityManagement直接继承IEntityMap或它的基本实现EntityMap类
4、还有业务外观和业务逻辑,调用EntityManagement,至于逻辑如多个实体对象利用COM+达到事务共享 因为各个对象在不同的数据访问层中提交,但很多时候需要事务,这里启动MSDTS5、用户界面层,直接申明实体如PersonEntiy并用EntityManagement如PersonManagement达到数据存取目的。PersonEntiy仅仅是根据成员读写数据,调用PersonManagement的增删改
这样,UI不像调用强类型的DataSet还要清楚字段名等,而直接以对象方式存取,屏蔽了数据结构。需要说明的是:
只要实现EntityAccess的IEntityMap接口,数据存取不用你写一行SQL,自动跨数据访问。代码如下:现在为简单起签,将实体定义直接包含实与数据映射需要看演示的朋友可以临时加QQ群(网友建)
群号:17556475您下载代码后可以主动退出群。
需要说明的是把示例代码上传到群上的时候还是采用以前的命名方法,但是也是这个思想,你只需更改配置文件(见readme.txt)无需更改代码就可以运行在Access、SQLServer、Oracle上。最后更新代码将在我的Blog上下载,并再加以文章说明。http://blog.csdn.net/flygoldfish
解决方案 »
- 在域账户下不能杀死EXCEL进程问题
- c# socket 粘包处理?
- 浏览过某商品的用户还浏览过的其他的产品 的存储过程怎么写
- 有关NHibernate的Session问题
- 如何参数化动态sql语句(普遍性的问题,顶着肯定有分,不够再加!!)
- 如何通过代码动态的将SQL中的某几个字段绑定到DataGridView?
- 如何调用远端的com+组件
- 在winform中,动态的给DataBase,配置了config文件,可编译后更改config文件配置无效,有具体说明,在线等,!!
- 如何得到一个字符串中的数字,有函数吗?
- c#winfrom出现字符串乱码问题
- 怎么在安装项目中把MSDE也打包进去,安装程序的时候把数据库也一起装上。
- 一个窗体上的按钮继承的问题?
/// 定义客户资料管理之客户对象数据。
/// </summary>
public class Product:EntityObjectBase
{
private long _ProductID;
private string _ProductName;
private int _SupplierID;
private int _CategoryID;
...
#region 属性.../// <summary>
/// 获取或设置产品ID。
/// </summary>
public long ProductID
{
get
{
return _ProductID;
}
set
{
_ProductID = value;
}
}
...
#endregion 属性.../// <summary>
/// 创建对象新的实例。
/// </summary>
public Product():base("Products","ProductID")
{
}public override IList GetFields()
{
string[] arrFields = new string[]{
"ProductID",
"ProductName",
... }; return arrFields;
}public override IList GetFieldValues()
{
object[] arrFieldValues = new object[]{
_ProductID,
_ProductName,
... }; return arrFieldValues;
}public override IList GetPrimaryKeyValues()
{
return new long[]{_ProductID};
}public override void LoadFrom(System.Data.DataRow entityDataRow)
{
_ProductID = long.Parse(entityDataRow["ProductID"].ToString());
_ProductName = entityDataRow["ProductName"].ToString();
...
}
}
/// 定义客户资料管理之客户对象数据。
/// </summary>
public class Product:EntityObjectBase
{
private long _ProductID;
private string _ProductName;
private int _SupplierID;
private int _CategoryID;
private string _QuantityPerUnit = "无";
private float _UnitPrice;
private int _UnitsInStock;
private int _UnitsOnOrder;
private int _ReorderLevel;
private bool _Discontinued;#region 属性.../// <summary>
/// 获取或设置产品ID。
/// </summary>
public long ProductID
{
get
{
return _ProductID;
}
set
{
_ProductID = value;
}
}/// <summary>
/// 获取或设置产品名称。
/// </summary>
public string ProductName
{
get
{
return _ProductName;
}
set
{
_ProductName = value;
}
}/// <summary>
/// 获取或设置供应商ID。
/// </summary>
public int SupplierID
{
get
{
return _SupplierID;
}
set
{
_SupplierID = value;
}
}/// <summary>
/// 获取或设置类别ID。
/// </summary>
public int CategoryID
{
get
{
return _CategoryID;
}
set
{
_CategoryID = value;
}
}/// <summary>
/// 获取或设置单位数量。
/// </summary>
public string QuantityPerUnit
{
get
{
return _QuantityPerUnit;
}
set
{
_QuantityPerUnit = value;
}
}/// <summary>
/// 获取或设置单价。
/// </summary>
public float UnitPrice
{
get
{
return _UnitPrice;
}
set
{
_UnitPrice = value;
}
}/// <summary>
/// 获取或设置库存量。
/// </summary>
public int UnitsInStock
{
get
{
return _UnitsInStock;
}
set
{
_UnitsInStock = value;
}
}/// <summary>
/// 获取或设置订购量。
/// </summary>
public int UnitsOnOrder
{
get
{
return _UnitsOnOrder;
}
set
{
_UnitsOnOrder = value;
}
}/// <summary>
/// 获取或设置再订购量。
/// </summary>
public int ReorderLevel
{
get
{
return _ReorderLevel;
}
set
{
_ReorderLevel = value;
}
}/// <summary>
/// 获取或设置中止标识。
/// </summary>
public bool Discontinued
{
get
{
return _Discontinued;
}
set
{
_Discontinued = value;
}
}#endregion 属性.../// <summary>
/// 创建对象新的实例。
/// </summary>
public Product():base("Products","ProductID")
{
}public override IList GetFields()
{
string[] arrFields = new string[]{
"ProductID",
"ProductName",
"SupplierID",
"CategoryID",
"QuantityPerUnit",
"UnitPrice",
"UnitsInStock",
"UnitsOnOrder",
"ReorderLevel",
"Discontinued"
}; return arrFields;
}public override IList GetFieldValues()
{
object[] arrFieldValues = new object[]{
_ProductID,
_ProductName,
_SupplierID,
_CategoryID,
_QuantityPerUnit,
_UnitPrice,
_UnitsInStock,
_UnitsOnOrder,
_ReorderLevel,
_Discontinued
}; return arrFieldValues;
}public override IList GetPrimaryKeyValues()
{
return new long[]{_ProductID};
}public override void LoadFrom(System.Data.DataRow entityDataRow)
{
_ProductID = long.Parse(entityDataRow["ProductID"].ToString());
_ProductName = entityDataRow["ProductName"].ToString();
_SupplierID = int.Parse(entityDataRow["SupplierID"].ToString());
_CategoryID = int.Parse(entityDataRow["CategoryID"].ToString());
_QuantityPerUnit = entityDataRow["QuantityPerUnit"].ToString();
//金额型为decimal
_UnitPrice = float.Parse(entityDataRow["UnitPrice"].ToString()); _UnitsInStock = int.Parse(entityDataRow["UnitsInStock"].ToString());
_UnitsOnOrder = int.Parse(entityDataRow["UnitsOnOrder"].ToString());
_ReorderLevel = int.Parse(entityDataRow["ReorderLevel"].ToString()); //bit、是否字段为布尔型,Oracle里没
//_Discontinued = bool.Parse(entityDataRow["Discontinued"].ToString());
if (entityDataRow["Discontinued"].ToString().ToUpper() == "1" || entityDataRow["Discontinued"].ToString().ToUpper() == "TRUE")
{
_Discontinued = true;
}
else
{
_Discontinued = false;
}}
}代码就这么多,即可实现跨数据存取
using WebMIS.COM.CN.Business.Entity;
using WebMIS.COM.CN.DAL;namespace WebMIS.COM.CN.Business.EntityManagement
{
/// <summary>
/// 对产品的增、删、改等管理。
/// </summary>
public sealed class ProductManagement:EntityObjectManagementBase
{
public ProductManagement()
{
} public ProductManagement(Product product):base(product)
{
} protected override IExeSql DoReturnDataAccessInstance()
{
return ExecuteSqlAdapter.GetInstance();
} public long GetMaxNextID()
{
return WebMIS.COM.CN.DAL.Util.GetMaxNextID(this.Entity.TableName,this.Entity.PrimaryKeys[0].ToString());
} }
}
小心地问一句,你的东西同微软的EnterpriseLibrary有区别吗?
我这里执行的SQL是由EntiyAccess里的工具自动生成的带参SQL,然后调用DataAccess,由它来处理不同数据库之间的差异。为了不与DataAccess偶合,所以加了一个IExcSQL接口,哈哈,这样两个dll就不至于在一块了。
说到跨数据库,项目是没有必要的,而做产品型的公司可能会用到。
再就是如果你是一个组件,可能会被 多家公司采用,那么这几家公司可能采用的不同数据库,而它却帮助了简化。说到多层,其它很多项目也没有必要分多层,特别是小的项目对付着的,能运行交付项目就行了,怎么快怎么来,很多软件公司也不考虑可维护性了。
不能奢望保证现在软件公司还能生存两生年,所以他们也不会去买组件也不会去采取多层设计:)但是作为程序员,建议大家不要个webform/winform都显示的申明SqlConnection/OracleConnection之类什么的,特别是一些刚做程序的朋友,先不说跨数据库的移植了,把它们专门二次封装一次对自己使用也方便。我这里只是说的实现原理与方式 ,大家可以探讨一下。
-----------------------------------------------------------------数据访问层要隐藏不同数据库之间的差异,你允许直接执行SQL语句其实等于没有隐藏数据库的差异,另外使用MSDTS也局限数据库的环境。最后问一句,会有几次整体更换数据库的机会,你这顶多算协助简化代码的数据访问帮助器,而并不是数据层,事实上,数据层并不一定是必须的,也不一定要先完成,也可以以后分化出来。
看了设计模式,一个工厂模式就搞定了
我用XML实际上定义的是Web/Win表单与数据存取之间的关系,也就是说只要你定义了这个XML
你不用写程序我就可以把Web/Win表单提交到数据库如有兴趣可以一起探讨,在17556475群共享中,有三层示例和我说的 Web/Win表单通用数据存取
http://blog.csdn.net/flygoldfish
1 对数据库的所有操作都可以通过你这个封装实现么?比如说多表级关联查询?
2 跨数据库支持,我自定义Sql语句的话,使用Oracle中独有的函数,你切换成Sql Server连接的时候能够自动转换函数实现么?如果以上,你都不能实现,那封装有何用?就拿出来骗骗小孩子么?
你认为这样做就可以了只能说明你对不同数据库的特性根本不了解。
但是有些是做产品的,或者组件是为各种人服务的,跨数据访问不得不考虑,尽量不用或少用存储,那些特性的函数也少用。总之,用就意味着多写代码,少用或不用就意味着失去效率。正如以时间换空间与以空间换时间一样,鱼与熊掌不可兼得。
可以提出合理与不合理的改进方便,这样才是初衷。
2. 代码复用性差,尤其是datatier 和 UI 。
3. DataTier 里除了sqlconnection ,剩下全是oledb, 为什么不用专用驱动连接器呢?
4. 业务写在什么地方?很乱。
。。楼主还需要继续努力,做好的架构还有距离。
不要拍砖~~
演示工程有说明,而且还有业务逻辑的目录,只为演示说明这个道理,因而UI没有更抽像。说DataTier 里除了sqlconnection ,剩下全是oledb, 为什么不用专用驱动连接器呢?
这只能说明你没看,应该是Oracle、Sql、OleDB、ODBC专用的,应用工厂模式。
1.只适用于简单的单表操作,复杂的处理仍然需要再写sql语句,比如跨表;
2.无法反映一对多,对对多的这种父子关系,也即并没有实现完全的对象化;
3.数据库字段名修改后需要修改代码并重新编译(这一点当然也可以改进实现);
4.楼主并没有考虑null情况下的默认值和转换异常;
5.简单的单数据库完全没有必要使用COM+,在你的架构中通过一些技巧就可以简单传递Transaction;
6.自动生成Sql语句会花费一定时间,不知道楼主有没有做过优化;
7.你的架构中,其实没有实现实体字段变更后的标志,所以每次都是全部的字段参与Sql构造,其实并不用,只需要更新更改后的字段即可;
8.多个记录的集合不知楼主如何处理的,如果用DataSet自然容易;其实于第2点有点类似,如果你真的实现了非常接近强类型DataSet的特性,那2和8的问题也不大,但仍然要考虑的是LazyLoad和数据关系同步,以及数据库操作的缓存和优化
暂时想到这么多,没仔细看过楼主的代码,有错误的地方还请指正
做程序讲究的是思路,写程序写的就是思想。我做这个其实只是为了方便自己,老实说,不太考虑性能问题,最主要是为了方便开发和维护。
我还有一个用XML做表单与数据库映射的,可以解决多表问题和数据库字段改后无需编译,维护也比较方便,同一个类也是应用工厂模式适用于webForm和WinForm。
在本贴的第一楼是public Product():base("Products","ProductID")
{
}public override IList GetPrimaryKeyValues()
{
return new long[]{_ProductID};
},如果是多个字段给成主键public Product():base("Products",new string[]{"字段A","字段B"...})
{
}public override IList GetPrimaryKeyValues()
{
ArrayList arrlist = new ArrayList();
arrlist.Add(此时字段A值 );
arrlist.Add(此时字段A值 );
return arrlist;
}
1.只适用于简单的单表操作,复杂的处理仍然需要再写sql语句,比如跨表;
是的,可以override实体管理类的增、删、改操作2.无法反映一对多,对对多的这种父子关系,也即并没有实现完全的对象化;
提供了一个实体集合,把它作为实体的一个成员,实现一对多3.数据库字段名修改后需要修改代码并重新编译(这一点当然也可以改进实现);
已有XML映射表单与数据库,些演示代码中没有,在群的另一演示中4.楼主并没有考虑null情况下的默认值和转换异常;
在演示中确实是这样的,在我的实际项目中有处理
public override IList GetFieldValues()
{
object[] arrFieldValues = new object[]{ _ID, ...
} GetValidID(kehu_id),
private object GetValidID(long id)
{ if (id == 0 || id == -1)
{
return null;
}
return id;
}在加载的时候作为判断if (entityDataRow["kehu_id"] != DBNull.Value)
{
_kehu_id = long.Parse(entityDataRow["kehu_id"].ToString());
}仅以此为例5.简单的单数据库完全没有必要使用COM+,在你的架构中通过一些技巧就可以简单传递Transaction;
是的,在一些情况下如GIS采用一个平台,现有系统又是一平台,两事务无法传递,些时采用6.自动生成Sql语句会花费一定时间,不知道楼主有没有做过优化;
自动生成Sql语句采用的是循环增加参数,采用参数化SQL,如Insert Into 表(字段a,字段b) Values (参数_字段a,参数_字段b),由IExeSql执行。拼SQL用的时间可能忽略不计,主要性能看IExeSql的实现者7.你的架构中,其实没有实现实体字段变更后的标志,所以每次都是全部的字段参与Sql构造,其实并不用,只需要更新更改后的字段即可;
确实如此8.多个记录的集合不知楼主如何处理的,如果用DataSet自然容易;其实于第2点有点类似,如果你真的实现了非常接近强类型DataSet的特性,那2和8的问题也不大,但仍然要考虑的是LazyLoad和数据关系同步,以及数据库操作的缓存和优化
提供的是实体数据存取,多个记录需要写在逻辑层中,应用循环调用每实体操作我这个只是实现行数据入口,还有表数据入口实现多记录,表入口很像孙亚民的Entiyt:DataSet。最后,谢谢大家讨论,特别谢谢pray1997(pray1997) 。
我也看过websharp,感觉你说的对属性什么的读写不太方便的问题并不存在吧!
在websharp1.1版本中,实体类是抽象类,虽然是抽象类,但对实体的操作与普通相同啊!反过来说,如果实体类不映身到数据结构,也就是数据结构暴露,那么实体映射些什么呢?上述仅代表个人意见,欢迎拍砖..
我不是说的websharp,我也没见过和用过。
我说的是以孙亚民的实体继承于DataSet的例子。
用DataSet作为各个层数据传输的载体确实是个好的方法,MS的经典示例也是用它。
这样,UI调用 它的时候,如果存取数据,免不了要dataRow["字段或别名"]
这样没有person.Name直接。实体类不映身到数据结构,也就是数据结构暴露,那么实体映射些什么呢?
实体类只定义实体的成员,如字段名叫ID,你可能定义实体的属性字段为“PersonID”。映射我是专门定义在EntityMap中的,可能是类实现,也可能是XML实现。如果你仅仅是项目自己用,不考虑对三方提供接口,实体映射可以定义在实体中。我的示例就是这种情况,并不是非要不暴露:)
再强调一下,孙亚民的那个架构确实非常好。
我还没看过,单凭卢彦认真和挚着,推荐一下。
http://www.agilelabs.cn
如果在WEB或者是WIN下,分页机制是否一样?
还有,楼主的这个架构有业务外观层吗?即BusinessFacaed层(DUWAMISH例子中)。目前我们的项目下BusinessFacaed层采用是WEBSERVICE,可以这个WEBSERVICE与分页碰到一起后,遇到了很大的问题,不好解决。
另外:楼主一定看过微软的EnterpriseLibrary吧,里面就有一个隔离各个数据库的层的例子,其实就是原来的SQLHELP的改进。还有楼主说的孙亚民的那个,其实可以参考VS2005下的强类型数据集的概念!微软其实在VS2005中给我们提供了一个类似于ORM的工具,不过要用好才行。
AF的架构我看过,我没看懂!还没有弄明白依赖注入等一些概念!
不过楼主的精神可嘉,我就没有心思写这些东西。