【问题标题】:Dynamic swappable Data Access Layer动态可交换数据访问层
【发布时间】:2011-02-16 19:43:31
【问题描述】:

我正在编写一个数据驱动的 WPF 客户端。客户端通常会从查询 SQL 数据库的 WCF 服务中提取数据,但我希望可以选择直接从 SQL 或其他任意数据源中提取数据。

我想出了这个设计,想听听你对它是否是最好的设计的意见。

首先,我们要从 SQL 中提取一些数据对象。

// The Data Object with a single property
public class Customer
{
    private string m_Name = string.Empty;

    public string Name 
    {
        get { return m_Name; }
        set { m_Name = value;}
    }
}

然后我计划使用一个所有数据访问层都应该实现的接口。假设也可以使用抽象类。想法?

// The interface with a single method
interface ICustomerFacade
{
    List<Customer> GetAll();
}

可以创建 SQL 实现。

// Sql Implementation
public class SqlCustomrFacade : ICustomerFacade
{
    public List<Customer> GetAll()
    {
        // Query SQL db and return something useful
        // ...

        return new List<Customer>();
    }
}

我们还可以创建 WCF 实现。 WCF 的问题在于它不使用相同的数据对象。它会创建自己的本地版本,因此我们必须以某种方式复制详细信息。我想可以使用反射来复制类似字段的值。想法?

// Wcf Implementation
public class WcfCustomrFacade : ICustomerFacade
{
    public List<Customer> GetAll()
    {
        // Get date from the Wcf Service (not defined here)
        List<WcfService.Customer> wcfCustomers = wcfService.GetAllCustomers();

        // The list we're going to return
        List<Customer> customers = new List<Customer>();

        // This is horrible
        foreach(WcfService.Customer wcfCustomer in wcfCustomers)
        {
            Customer customer = new Customer();
            customer.Name = wcfCustomer.Name;
            customers.Add(customer);
        }

        return customers;
    }
}

我还计划使用工厂来决定使用哪个外观。

// Factory pattern
public class FacadeFactory()
{
    public static ICustomerFacade CreateCustomerFacade()
    {
        // Determine the facade to use
        if (ConfigurationManager.AppSettings["DAL"] == "Sql")
            return new SqlCustomrFacade();
        else
            return new WcfCustomrFacade();
    }
}

这就是 DAL 通常的使用方式。

// Test application
public class MyApp
{
    public static void Main()
    {
        ICustomerFacade cf = FacadeFactory.CreateCustomerFacade();
        cf.GetAll();
    }
}

感谢您的想法和时间。

【问题讨论】:

  • 正如@TomTom 强调的那样,Linq 在生成业务对象和在 SQL 外观中查询方面将节省大量时间。此示例中的对象仅用于说明概念。

标签: c# wcf reflection oop polymorphism


【解决方案1】:

有两种方法可以解决 WCF 实现中的数据对象与从数据存储区返回的内容之间的差异:

  • 创建 WCF 代理时,请确保重用引用程序集中的所有类型(如果使用 VS 选项而不是直接调用 svcutil,则在“高级”对话框中)。

  • 在您的 DTO(数据对象)上有一个 Clone() 或 CopyFrom() 类型方法,以便您可以将对象从本地生成的命名空间映射到常规项目命名空间,然后再返回

我会实施选项 1 - 选项 2 肯定会起作用,但这样做的速度非常慢。偶尔当你告诉 VS 在生成代理时重用引用的类型时,它仍然会生成 DTO 的本地定义 - 在这种情况下,你可以进入生成的 Reference.cs 类文件并删除所有定义,然后继续使用在常规项目命名空间中定义的版本。

除了 WCF 问题之外,您似乎走在正确的轨道上。

【讨论】:

  • 感谢您的 cmets。选项 1 听起来可以解决问题!
【解决方案2】:

您有一个非常灵活的软件方法的良好开端。您的方法已经遇到了主要问题:您的数据提供者合同 (ICustomerFacade) 必须指定所有实施者都使用的数据对象。 SQL 和 WCF 数据提供程序都必须返回相同的数据对象。

你标记为“这太可怕了”的那部分?实际上并不是那么糟糕。是的,您进行了两次迭代,但这样做是为了提供更强大、更灵活的软件架构。性能不会那么差(除非您要迭代列表中的许多项目),并且您的系统将能够在调用 Web 服务和直接调用 SQL Server 之间切换(无论是否总的来说,这是一个好主意)随意。

消除双重迭代可以做的一件事是让您的数据契约依赖于数据对象的抽象。例如,他们会返回 ICustomer 而不是 Customer。那么您的 SQL Server 对象和您的 WCF 数据对象可以完全是不同的对象,只要它们实现了ICustomer 等接口。

其他建议:

  • 您应该考虑为您的集合返回方法返回IList(甚至IEnumerable)而不是List
  • 你的工厂模式是一个好的开始,但是工厂是 2000 年代。 :) 您可能需要考虑采用完整的依赖注入路线;我建议在 Microsoft Enterprise Library 中使用 Unity。

【讨论】:

  • 非常有用的cmets。非常感谢。
【解决方案3】:

如果您将 .NET 与 .NET 对话,我个人认为 svcutil.exe 是一种反模式。这是一个没有实际价值的额外突破点。如果您要与外部服务或不同平台集成,svcutil.exe 是一个不错的选择。

通常,对于每个服务,都应该有一个附加的 Contracts 程序集,其中包含该服务的所有服务接口和数据合同。该程序集被服务和客户端引用。这样,当进行更改时,它们将同时反映在客户端和服务器中。

http://blog.walteralmeida.com/2010/08/wcf-tips-and-tricks-share-types-between-server-and-client.html

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-08
    • 2010-11-19
    • 2011-07-24
    • 2011-10-20
    • 2011-03-16
    • 2011-02-17
    • 2011-08-02
    相关资源
    最近更新 更多