如何将对象类型从业务层传递到数据层
Posted
技术标签:
【中文标题】如何将对象类型从业务层传递到数据层【英文标题】:How to pass the object type from business layer to data layer 【发布时间】:2012-04-03 10:20:10 【问题描述】:我正在创建 n 层应用程序架构。不同的层对彼此的内部实现一无所知,层之间的通信通过非常狭窄的接口处理,并辅以 IoC/DI。
现在我将业务对象(在业务层中定义)传递给数据层。业务层本身不知道数据实际保存的方式和位置(通常是数据库,但业务层不应该知道)。业务对象通过 IDataReader 的实现传递到数据层(这种方法还支持在未来场景中批量加载数据)。数据层从 IDataReader 读取所有数据并调用一些存储过程将数据保存到数据库(如果保存一个对象,IDataReader 返回“一行”)。
实际问题在这里:
因为我只是将 IDataReader 传递给数据层并断开数据与其类型的连接,在这种情况下确定数据位置的最佳方法是什么?
例如,如果我将“用户”类型的实际业务对象传递给数据层,那么数据层应该将数据保存到表“用户”中。在其他一些情况下,数据可能会独立于业务层中的类型保存到不同的结构中。
在当前的实现中,我将业务对象的类型信息传递到数据层,数据层检查该类型并决定数据的放置位置。
数据层检查传入数据并确定其位置是正确的解决方案,还是数据层应该公开可以保存数据的“位置”列表(枚举?)?
提前致谢!
/澄清:
我在这里看到的选项是: 1.数据层给出可以保存数据的“plces”列表 2. 数据层检查给定的参数(如类型 arg)并决定存储数据的位置
第一选择罚分;如果我尝试将“产品”类型的业务对象存储到“用户”类型通常使用的结构中怎么办
二选一罚分;我必须将类型“Namespace1.Namespace2.User”映射到将其数据保存到表“User”的特定例程。所以手动为每个类型做一些映射......
/澄清 2:
现在我确实像这样检索:
service.Retrieve(typeof(Catalog), null, catalogArgs, e => catalogConverter.Read(e));
所以我将 typeof(Catalog) 传递给数据层......所以我在数据层中有类型信息。
现在在数据层中,我需要选择“适配器”来努力从数据库中获取数据。我可以编写巨大的 if/switch 结构来选择适配器……这令人沮丧。我也可以编写属性并像这样使用它:
[TypeAdapterAttribute("Namespace1.Namespace2.Catalog, and assembly info...")]
class CatalogAdapter ...
然后我有从给定类型映射到属性值的代码...
...但是硬编码类型字符串有点臃肿和问题...
有什么建议吗...?
/澄清 3
我知道这个系统可以通过“可插拔的业务模块”来扩展。这些模块包含基本业务逻辑不知道的业务对象(和一些逻辑),数据层也完全不知道“插入程序集”中包含的这些业务对象。这个外部业务模块没有引用基础业务层或数据层。当保存来自该外部程序集的业务对象(=发送到数据层)时,数据层默认将其保存为 EAV 样式(http://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80% 93value_model) 数据结构自动。因为这种数据结构可能会存在一些性能问题,所以数据层必须有办法将特定的业务对象类型保存在自己专用的数据结构中(通常是与该类型一对一映射的表)。所以问题来了,如何实现这个“选择决策”:)
这里的想法是我可以创建许多新的业务对象,并且我可以将它们全部保存到数据层,而无需对数据层进行任何实际编码!以后如果需要,我可以为选定的业务对象创建自己的专用数据结构。
【问题讨论】:
澄清 3 描述了全新的先决条件并提出了全新的问题。您可以根据属性的数量做出决定。少数属性-> EAV,许多属性-> 专用模型。您可以像这样确定属性PropertyInfo[] props = obj.GetType().GetProperties();
嗯,C3 仍然是同一个系统。我只是试图澄清系统的目标。我想我可能会在我稍微考虑一下之后提出新问题......
【参考方案1】:
我建议您使用 ORM(实体框架,NHibernate)或微 ORM(PetaPoco,Dapper)将您的对象映射到数据存储。 如果您有具体问题,请查看并拍摄。
更新:我想我刚刚明白了你的要求。 您需要在数据层中为每种类型定义一个新方法。 所以:
public User GetUserById(int id);
public void SaveUser(User user);
public Product GetProductById(int id);
public void SaveProduct(Product product);
【讨论】:
如果使用任何 ORM,我仍然需要知道如何“从 orm 中选择实体用于数据存储”... 因为任何实现都是特定于 ORM 的,所以我不能给你一个通用的代码示例。您想要做的(将对象映射到表)是 ORM 的全部目的。我想说,看看 PetaPoco,因为它非常简单。该文档是一个完整的代码示例页面。 @Harza:是的,但是您不考虑表和列,而是考虑业务对象及其属性。请参阅上面的示例。 @Olivier:完全正确。这就是映射的用武之地,因为它将列映射到属性(在数据层中)。从那里你只处理对象的属性。【参考方案2】:如果您想创建自己的与业务层断开连接的数据库层,您还可以引入带有合同的单独程序集
List<Customer> customers = DB.LoadCustomers();
数据层可以使用泛型类型参数并通过Reflection
获取有关业务对象的信息。这提供了良好的层分离,因为数据层不需要引用业务组件。
List<Customer> customers = DataContext.Query<Customer>.Load();
或
Customer customer = DataContext.Query<Customer>.Load(custID);
O/R-mappers 通常是这样工作的
List<Customer> customers = Context.Query<Customer>()
.Where(cust => cust.Name.StartsWith("A"))
.OrderBy(cust => cust.Name)
.ToList();
在这些示例中,数据层创建并填充业务对象。否则,业务层必须知道表列名称。
(注意:我这里不是指具体的数据接口。)
更新:
如果您想创建自己的与业务层断开连接的数据库层,您还可以引入带有合同的单独程序集
public interface ICustomer
string LastName get; set;
string FirstName get; set;
...
数据层和业务层都会引用这些合同。
在数据层,你会有一个类似的方法
public List<T> LoadAll<T>(Func<T> create)
var list = new List<T>();
if (T is ICustomer)
string sql = "SELECT * FROM tblCustomer";
...
while (reader.NextResult())
ICustomer cust = (ICustomer)create();
cust.FirstName = reader.GetString("FirstName");
...
list.Add((T)cust);
else if (T is IOrder)
...
return list;
在你要写的业务层
List<ICustomer> customers = DbLayer.LoadAll<ICustomer>(() => new Customer());
现在,您的数据层可以与客户一起工作,而无需了解您的客户类别,也无需参考业务层程序集。
【讨论】:
解决方案试图避免对象映射,使用 IDataReader 通过层传输数据。在查询案例中,数据层将 IDataReader 提供给业务层,可用于在需要时读取数据和物化对象。使用 IDataReader 没有问题,只是“如何告诉数据层在没有实际类型的情况下处理的类型” 可能是这样的:IDataReader customers = DataLayer.LoadAll("Customers");
。但是,您的业务层必须知道列名。
在您的示例中,您通过字符串文字“客户”告诉您需要获取什么样的数据,这是这个问题的关键点。现在我正在做类似的事情,但使用 System.Type 作为参数。然后,数据层以某种方式确定了获取“客户”的方式。我只是尝试在这里解决最合适的解决方案(字符串,类型,其他)......
cust.GetType().Name
or typeof(Customer).Name
or FullName
如果您需要命名空间。
您提到的辛勤工作正是 o/r-mappers 为您所做的!你想要做的实际上是一个映射。您无法避免映射。映射是将属性分配给列、将类分配给表的过程,反之亦然。这在任何情况下都必须通过 o/r-mapper 或您的代码来完成。以上是关于如何将对象类型从业务层传递到数据层的主要内容,如果未能解决你的问题,请参考以下文章