我的同事和我正在为在线店面整理一个小型报告框架。我们在存储库模式之后构建了一个库,使用“reports”作为存储库,并使用一个非常轻的服务层来与所述报告进行交互。我们的代码非常好用,并且非常易于使用。但是,有一件事情困扰着我个人:在服务工厂层面,我们需要两次声明我们的返回类型(它没有被推断)。这是我们项目的基础:
报告界面
这就是我们的“存储库”。它们接受数据访问对象,例如SQL / Oracle连接的包装类,或者我们的Storefront API。
internal interface IReport<T>
{
T GetReportData(dynamic options);
}
存储库工厂
这提供了一种通过了解类型生成这些报告的简便方法。
internal interface IReportFactory
{
TR GenerateNewReport<T, TR>() where TR : IReport<T>;
}
internal class ReportFactory : IReportFactory
{
public ReportFactory()
{
// some initialization stuff
}
public TR GenerateNewReport<T, TR>() where TR : IReport<T>
{
try
{
return (TR)Activator.CreateInstance(typeof(TR));
}
catch(Exception ex)
{
// Logging
}
}
}
示例报告(存储库)
以下是报告的样子。请注意,它返回一个DataTable,并在通用接口中声明它(这将很快出现)。
internal class ItemReport : IReport<DataTable>
{
public DataTable GetReportData(dynamic options)
{
return new DataTable();
}
}
报告服务
一种轻量级服务,它接收报告(存储库)并使用它。轻巧,但它允许简单的单元测试等,如果我们想要在检索报告后添加额外的处理,那么这样做很容易。
public interface IReportService<T>
{
T GetReportData(dynamic options);
}
public class ReportService<T> : IReportService<T>
{
private readonly IReport<T> _report;
public ReportService(IReport<T> report)
{
_report = report;
}
public T GetReportData(dynamic options)
{
return _report.GetReportData(options);
}
}
服务工厂
我们将服务工厂设置为抽象类(因为所有服务工厂都需要创建报告工厂),强制使用默认构造函数:
public abstract class ReportServiceFactory
{
protected IReportFactory ReportFactory;
protected ReportServiceFactory(connection strings and other stuff)
{
ReportFactory = new ReportFactory(connection strings and other stuff);
}
}
然后我们可以根据功能创建单独的服务工厂。例如,我们有一个“标准报告”服务工厂,以及客户特定的服务工厂。这里的实现是我的问题所在。
public class SpecificUserServiceFactory : ReportServiceFactory
{
public SpecificUserServiceFactory(connection strings and other stuff) : base(connection strings and other stuff){}
public IReport<DataTable> GetItemReport()
{
return new ReportService<DataTable>(ReportFactory.GenerateNewReport<DataTable, ItemReport>());
}
}
为什么创建服务工厂时我必须如此冗长?我两次声明返回类型。为什么我不能这样做:
return new ReportService(ReportFactory.GenerateNewReport<ItemReport>());
请注意,我没有在此声明DataTable;我认为应该从ItemReport是IReport的事实推断出来。任何关于如何以这种方式工作的建议都将不胜感激。
很抱歉这么长的解释要问这么简单的问题,但我认为所有支持代码都有助于提出解决方案。再次感谢!
答案 0 :(得分:1)
我认为编译器无法从DataTable
推断ItemReport
。但是,您可以通过在非泛型类中使用静态泛型方法来避免两次指定DataTable
。
ReportService.Create(reportFactory.GenerateNewReport<DataTable, ItemReport>())
public static class ReportService
{
public static ReportService<T> Create<T>(IReport<T> report)
{
return new ReportService<T>(report);
}
}
答案 1 :(得分:1)
在调用GenerateNewReport时无法省略DataTable泛型类型的原因是因为它是该函数定义中其他泛型类型的约束。让我们假设(为简单起见)您的IReport&lt; T&gt;实际上是一个带有函数void Something(T输入)的接口。一个类可以实现IReport&lt; int&gt;和IReport&lt; string&gt;。然后,我们构建了一个名为Foo的类:IReport&lt; int&gt ;, IReport&lt; string&gt;。编译器将无法编译bar.GenerateNewReport&lt; Foo&gt;因为它不知道它是否绑定到IReport&lt; int&gt;或IReport&lt; string&gt;类型,因此无法确定该调用的适当返回类型。