用于将各种源类型和各种目标类型导入数据的设计模式


14

我必须设计和构建可以处理以下内容的导入脚本(在C#中):

  • 从各种来源(XML,XSLX,CSV)读取数据
  • 验证数据
  • 将数据写入各种对象类型(客户,地址)

数据将来自多个来源,但一个来源将始终具有一种导入格式(csv,xml,xslx)。导入格式因来源而异。将来可能会添加新的导入格式。目标对象类型始终相同(客户,地址和其他)。

我一直在考虑使用泛型,并且阅读了一些有关工厂模式的信息,但是我在这个领域非常菜鸟,所以任何建议都值得欢迎。

解决该问题的合适设计模式是什么?


把事情简单化。
NoChance

Answers:


11

您太花哨的概念太过早了。泛型-当您看到案例时,可以使用它们,但不要担心。工厂模式-为此还具有太多的灵活性(并增加了混乱)。

把事情简单化。使用基本做法。

  1. 尝试想象一下在进行XML读取与CSV读取之间的共同点。诸如下一条记录,下一行之类的内容。由于可能会添加新格式,因此请尝试想象待确定格式与已知格式的共性。使用此通用性,定义所有格式都必须遵守的“接口”或合同。尽管他们坚持共同立场,但他们都有各自特定的内部规则。

  2. 为了验证数据,请尝试提供一种轻松插入新的或不同的验证器代码块的方法。因此,再次尝试定义一个接口,每个负责特定类型数据构造的验证器均应遵守合同。

  3. 对于创建数据结构,无论谁设计建议的输出对象,您可能都会受到约束。尝试弄清楚数据对象的下一步是什么,通过了解最终用途可以进行任何优化。例如,如果您知道对象将在交互式应用程序中使用,则可以通过提供对象的“总和”或计数或其他种类的派生信息来帮助该应用程序的开发人员。

我会说大多数都是模板模式或策略模式。整个项目将是一个Adapter模式。


+1,尤其是对于第一段(很高兴看到您得出的结论与我在最后一段中相同)。
布朗

还请记住整个项目的架构,以使一种格式适应另一种格式。您能想象有人在另一个项目中仅使用其中一部分的情况吗?EG也许市场上有一种新的数据验证器,它仅适用于SQL Server。因此,现在您只需要读取自定义XML并放入SQL Server中,而跳过其余步骤。
安迪斯·史密斯

为了促进这一点,不仅要使作品具有遵守的内部合同,还应该有一组合同来定义作品之间的相互作用。
安迪斯·史密斯

@AndyzSmith-我的代码中有相同的问题。除了Adapter模式之外,我了解您的所有代码。当您说整个项目是Adapter模式的示例时,您可以说明一下吗?
gansub

9

显而易见的是应用策略模式。具有通用的基类ReadStrategy,对于每种输入格式,都有一个子类,例如XmlReadStrategyCSVReadStrategy等等。这将使您可以独立于验证处理和输出处理来更改导入处理。

根据详细信息,也可以保留导入通用的大部分内容,而仅交换输入处理的一部分内容(例如,读取一条记录)。这可能会导致您进入“ 模板方法”模式


这是否意味着在使用策略模式时,我必须创建用于将对象(客户,地址)从源转换为目标的单独方法。我想做的是读取,转换和验证每个对象,并将其放在列表中,以便以后可以将列表保存到数据库中。
2013年

@jao:好吧,如果您再次阅读我的答案,您会发现我的建议是创建“ ReadStrategy”,而不是“ ConvertStrategy”。因此,您只需要编写不同的方法来读取对象(或过程的其他任何部分对于特定的文件格式而言都是单独的)。
布朗

7

对于将来可能需要扩展的导入实用程序,合适的模式是使用MEF-您可以通过从惰性列表中动态加载所需的转换器来保持较低的内存使用量,创建装饰有属性的MEF导入可以帮助您为要尝试执行的导入选择正确的转换器,并提供一种简便的方法来区分不同的导入类。

可以使用一些标准方法来构建每个MEF部件,以使其满足导入接口,这些标准方法会将导入文件的行转换为输出数据,或者使用基本功能覆盖基类。

MEF是用于创建插件体系结构的框架-它是如何构建Outlook和Visual Studio的,而VS中所有这些可爱的扩展都是MEF的一部分。

要构建MEF(托管可扩展性框架)应用,请先添加对 System.ComponentModel.Composition

定义接口以指定转换器将执行的操作

public interface IImportConverter
{
    int UserId { set; }        
    bool Validate(byte[] fileData, string fileName, ImportType importType);
    ImportResult ImportData(byte[] fileData, string fileName, ImportType importType);
}

这可以用于要导入的所有文件类型。

将属性添加到新类中,以定义该类将“导出”的内容

[Export(typeof(IImportConverter))]
[MyImport(ImportType.Address, ImportFileType.CSV, "4eca4a5f-74e0")]
public class ImportCSVFormat1 : ImportCSV, IImportConverter
{
 ...interface methods...
}

这将定义一个类,该类将导入CSV文件(特定格式:Format1),并具有用于设置MEF导出属性元数据的自定义属性。您要为要导入的每种格式或文件类型重复此操作。您可以使用以下类来设置自定义属性:

[MetadataAttribute]
[AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
public class ImportAttribute : ExportAttribute
{
    public ImportAttribute(ImportType importType, ImportFileType fileType, string customerUID)
        : base(typeof(IImportConverter))
    {
        ImportType = importType;
        FileType = fileType;
        CustomerUID = customerUID;
    }

    public ImportType ImportType { get; set; }
    public ImportFileType FileType { get; set; }
    public string CustomerUID { get; set; }
}

要实际使用MEF转换器,您需要导入在运行转换代码时创建的MEF部件:

[ImportMany(AllowRecomposition = true)]
protected internal Lazy<IImportConverter, IImportMetadata>[] converters { get; set; }
AggregateCatalog catalog = new AggregateCatalog();

catalog 从文件夹收集零件,默认为应用程序位置。

converters 是进口的MEF零件的懒惰清单

然后,当您知道要转换的文件类型(importFileTypeimportType)时,可以从导入的零件列表中获取转换器。converters

var tmpConverter = (from x in converters
                    where x.Metadata.FileType == importFileType
                    && x.Metadata.ImportType == importType 
                    && (x.Metadata.CustomerUID == import.ImportDataCustomer.CustomerUID)
                    select x).OrderByDescending(x => x.Metadata.CustomerUID).FirstOrDefault();

if (tmpConverter != null)
{
     var converter = (IImportConverter)tmpConverter.Value;
     result = converter.ImportData(import.ImportDataFile, import.ImportDataFileName, importType);
....
}

的调用converter.ImportData将使用导入的类中的代码。

可能看起来像很多代码,可能需要花费一些时间来弄清正在发生的事情,但是在添加新的转换器类型时它非常灵活,甚至可以允许您在运行时添加新的转换器类型。


我以前从未听说过MEF。它是什么?
2013年

2
@jao查看完整链接。在我的答案中添加了一些示例MEF内容。
马特

1
这是启动MEF的绝佳方法。+1
paqogomez

MEF是一种技术,而不是设计模式。-1我不接受,因为基本思想仍然有意义,并且依赖于IImportConverter界面所统治的策略模式。
GETah

0

解决该问题的合适设计模式是什么?

C#习惯用法涉及使用内置的序列化框架来执行此操作。您可以使用元数据为对象添加注释,然后实例化使用这些注释的各种序列化程序,以将数据提取为正确的格式,反之亦然。

Xml,JSON和二进制形式是最常见的形式,但是如果其他形式已经以包装好的形式供您使用,我也不会感到惊讶。


好吧,如果您可以自由使用自己的文件格式,则此方法效果很好,但是我想这种方法将无法用于复杂的预定义格式(例如XSLX),这意味着压缩XML格式的MS Excel文件。
布朗

我可以将一行Excel文件映射到一个对象,但是我需要将该方法复制并改编为XML和CSV阅读器。而且我想保持代码尽可能的整洁……
jao

@docBrown-怎么样?从概念上讲,在Excel中将对象转换为一系列单元格与将其转换为xml文档没有什么不同。
Telastyn

@Telastyn:您说可以使用.NET框架的内置序列化框架来读取XLSX格式吗?如果这是真的,那么像Open XML SDK或NPOI这样的库就已经过时了。
布朗

@docbrown:对不起,您是对的-我一直忘了没有通用的序列化器基类,因为这是我工作的任何代码库中要做的第一件事之一
。– Telastyn
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.