如何将对象类型从业务层传递到数据层

3
我正在创建n层应用程序架构。不同的层不知道彼此的内部实现,层之间的通信通过非常窄的接口进行,并辅以IoC/DI。
现在我将业务对象(在业务层中定义)传递到数据层。业务层本身并不知道数据实际上是如何保存的(通常是数据库,但业务层不应该知道)。业务对象通过IDataReader的实现传递到数据层(这种方法还支持未来情况下的批量加载数据)。数据层从IDataReader中读取所有数据,并调用一些存储过程将数据保存到数据库中(如果保存一个对象,则IDataReader返回“一行”)。
实际问题在于:
因为我只是将IDataReader传递给数据层,并且将数据与其类型分离,所以在这种情况下确定数据位置的最佳方式是什么?
例如,如果我将类型为“User”的实际业务对象传递到数据层,则数据层应将数据保存到表“User”中。在某些其他情况下,数据可能会独立于业务层中的类型而保存到某个不同的结构中。
在当前实现中,我将业务对象的类型信息传递到数据层,数据层检查此类型并决定将数据放置在哪里。
数据层检查传入的数据并决定其位置是否正确,或者数据层应公开可以保存数据的“位置”列表(枚举)?
提前感谢!
澄清:
我看到的选项是: 1.数据层提供可以保存数据的“位置”列表 2.数据层检查给定的参数(如类型参数)并决定存储数据的位置
第一种选择的惩罚是什么?如果我尝试将类型为“Product”的业务对象存储到通常由类型“User”使用的结构中怎么办?
第二个选项的惩罚是什么?我必须将类型“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(); - Olivier Jacot-Descombes
嗯,C3仍然是同一系统。我只是试图澄清系统的目标。我想我可能会在思考一下之后开新问题... - Harza
2个回答

1
如果您想创建一个与业务层分离的自己的数据库层,您也可以引入一个带有合同的单独程序集。
List<Customer> customers = DB.LoadCustomers();

数据层可以使用泛型类型参数,并通过反射获取有关业务对象的信息。这提供了良好的层次分离,因为数据层不需要引用业务组件。
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 customers = DataLayer.LoadAll("Customers");。但是,你的业务层必须知道列名。 - Olivier Jacot-Descombes
在你的例子中,你通过字符串常量“Customers”告诉了需要获取什么类型的数据,这是这个问题的关键点。现在我正在做类似的事情,但是使用System.Type作为参数。数据层必须确定获取“Customers”的方式。我只是试图解决最合适的解决方案(字符串、类型、其他)。 - Harza
你所提到的辛勤工作,正是O/R映射器为你所做的!你所尝试的实际上是一种映射。你无法避免映射。映射是将属性分配给列、类分配给表等的过程,反之亦然。这必须在任何情况下完成,无论是通过O/R映射器还是通过你的代码。 - Olivier Jacot-Descombes
但是这样做会将业务对象绑定到特定的OR-mapper,而且数据层也必须是那种类型吗?我试图创建一个具有“服务层”的数据层,该层充当数据层的门户,在其后面可能是orm或其他东西(也许是Active Directory或Web Service)...关键点在于不是我可能在数据层中使用什么技术,而是如何通过业务层中的类型选择特定的技术...我知道这很难解释,感谢所有回复!! - Harza
澄清3解释了为什么“第三条腿”不适用于这里 :) - Harza
显示剩余4条评论

0
我建议您使用ORM(Entitiy Framework,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中选择实体以用于数据存储”... - Harza
由于任何实现都是特定于ORM的,我无法为您提供通用的代码示例。您想要做的事情(将对象映射到表)是ORM的全部目的。我建议看看PetaPoco,因为它非常简单。文档只有一页,充满了代码示例。 - David
@Harza:是的,但你不是按照表格和列来思考,而是按照你的业务对象及其属性来思考。请参考我上面的例子。 - Olivier Jacot-Descombes
@Olivier:没错。这就是映射的作用,它将列映射到数据层中的属性。从那里开始,您只需处理对象的属性即可。 - David

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接