设计数据模型以进行平面文件转换...委托还是继承?

发布于 2024-07-30 09:25:27 字数 2511 浏览 2 评论 0原文

我有一个维护应用程序,必须将企业数据(来自各种数据库/表)转换为平面文件,每个文件都采用特定的格式,以供遗留应用程序使用。 我有数据模型,就像

public class StatusCode
{
    public String Id { get; set; }
    public Char Level { get; set; }
    public String Description { get; set; }
}

我将从数据源中选择一些子集或所有这些记录一样。 我需要将每个实体映射到文件的一行,这可能需要调整数据(填充、转换或处理 null)。

public delegate String MapEntity<T>(T entity);
public MapEntity<StatusCode> MapStatusCode = delegate(StatusCode entity)
{
    return String.Format("{0},{1},{2}",
        entity.Id.PadLeft(3, '0'),
        entity.Level == 'S' ? 0 : 1,
        entity.Description ?? "-");
}

问题是,如何编写转换类? 我是否提供一个接受映射委托的“DefaultFileCreator”?

public interface IFileCreator
{
    Byte[] Create<T>(MapEntity<T> map, IEnumerable<T> entities);
}

public class DefaultFileCreator : IFileCreator
{
    public Byte[] Create<T>(MapEntity<T> map, IEnumerable<T> entities)
    {
        StringBuilder sb = new StringBuilder();
        foreach (T entity in entities)
            sb.AppendLine(map(entity));

        return Encoding.Default.GetBytes(sb.ToString());
    }
}

...
fileCreator.Create(MapStatusCode, repository<StatusCode>.FindAll());
...

通过此解决方案,我关心应该在何处以及在什么范围内保留映射委托。 以及我将如何在不知道 T 的情况下调用它们(如果我需要的话)。

或者,我是否更改接口并需要具体类中的映射?

public interface IFileCreator<T>
{
    Byte[] Create(IEnumerable<T> entities);
}

public abstract class FileCreator : IFileCreator<T>
{
    protected abstract String Map(T entity);

    public Byte[] Create(IEnumerable<T> entities)
    {
        StringBuilder sb = new StringBuilder();
        foreach (T entity in entities)
            sb.AppendLine(Map(entity));

        return Encoding.Default.GetBytes(sb.ToString());
    }
}

public class StatusCodeFile : FileCreator<StatusCode>
{
    public override String Map(T entity)
    {
        return String.Format("{0},{1},{2}",
            entity.Id.PadLeft(3, '0'),
            entity.Level == 'S' ? 0 : 1,
            entity.Description ?? "-");
    }
}

该解决方案在具体类中爆炸,但它们与映射委托一样薄。 而且我觉得与 IFileCreator 和工厂一起工作更舒服。 (再次强调,仅在必要时)。

我假设某些基类很有用,因为 StringBuilder 循环和 Byte[] 编码很简单。 具体类是否应该在基类中设置委托属性(而不是调用抽象方法)? 我应该保留方法上的类型参数(这将如何影响基类/具体类)?

我愿意寻求任何解决方案。 我的主要目标是易于维护。 我现在有 12 个模型/文件,这可能会增加到 21 个。我可能需要在任何文件中插入任意页眉/页脚行(这就是为什么我喜欢可重写的基类方法 Map)。

I have a maintenance application that has to turn enterprise data (from various databases/tables) into flat files, each in a specific format, for consumption by a legacy application. I've got data models like

public class StatusCode
{
    public String Id { get; set; }
    public Char Level { get; set; }
    public String Description { get; set; }
}

I will select some subset or all of these records from the data source. And I need to map each entity to one line of the file, which may require adjusting the data (padding, transforming, or handling null).

public delegate String MapEntity<T>(T entity);
public MapEntity<StatusCode> MapStatusCode = delegate(StatusCode entity)
{
    return String.Format("{0},{1},{2}",
        entity.Id.PadLeft(3, '0'),
        entity.Level == 'S' ? 0 : 1,
        entity.Description ?? "-");
}

The question is, how do I write the transformation classes? Do I provide a "DefaultFileCreator" that takes a mapping delegate?

public interface IFileCreator
{
    Byte[] Create<T>(MapEntity<T> map, IEnumerable<T> entities);
}

public class DefaultFileCreator : IFileCreator
{
    public Byte[] Create<T>(MapEntity<T> map, IEnumerable<T> entities)
    {
        StringBuilder sb = new StringBuilder();
        foreach (T entity in entities)
            sb.AppendLine(map(entity));

        return Encoding.Default.GetBytes(sb.ToString());
    }
}

...
fileCreator.Create(MapStatusCode, repository<StatusCode>.FindAll());
...

With this solution, I'm concerned about where and in what scope I should keep the mapping delegates. And how I'm going to call them without knowing T (if I ever need to).

Or, do I change the interface and require the mapping in the concrete classes?

public interface IFileCreator<T>
{
    Byte[] Create(IEnumerable<T> entities);
}

public abstract class FileCreator : IFileCreator<T>
{
    protected abstract String Map(T entity);

    public Byte[] Create(IEnumerable<T> entities)
    {
        StringBuilder sb = new StringBuilder();
        foreach (T entity in entities)
            sb.AppendLine(Map(entity));

        return Encoding.Default.GetBytes(sb.ToString());
    }
}

public class StatusCodeFile : FileCreator<StatusCode>
{
    public override String Map(T entity)
    {
        return String.Format("{0},{1},{2}",
            entity.Id.PadLeft(3, '0'),
            entity.Level == 'S' ? 0 : 1,
            entity.Description ?? "-");
    }
}

This solution explodes in concrete classes, but they're as thin as the mapping delegates. And I feel more comfortable working with IFileCreator<T> and a factory. (again, only if necessary).

I'm assuming some base class is useful as the StringBuilder loop and Byte[] encoding are straightforward. Should the concrete class set a delegate property in the base class (rather than calling an abstract method)? Should I keep the type parameter on the method (and how would that affect the base/concrete classes)?

I'm up for any solution. My main goal is ease of maintenance. I have 12 models/files right now and this may increase up to 21. I may have a requirement to insert arbitrary header/footer lines in any file (which is why I like the overrideable base class method, Map).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

遗弃M 2024-08-06 09:25:27

您是否必须为每个可能的映射实际创建具体的子类? 也许可以使用 XML 文件(或数据库)来描述每种文件的格式/内容。 然后,您将拥有一个类,该类采用“FileType”键并使用 XML 中的格式信息来确定如何为该 FileType 构建文件。

Do you have to actually create concrete subclasses for each possible mapping? Maybe it would be possible to instead use an XML file (or a database) to describe the format/contents of each kind of file. You then have a single class that takes a "FileType" key and uses the formatting information from the XML to determine how to build the file for that FileType.

梦幻的味道 2024-08-06 09:25:27

现在我已经编写了一些转换,我倾向于每类映射方法。 我必须能够针对伪造的模型运行单元测试,以确保文件正确构建(针对已知的良好示例文件进行测试)。

我一直将映射委托设为它们所属的“工作流”类的私有(每个模型/文件一个“工作流”)。 我必须将它们公开才能对它们进行单元测试,或输出中间文件内容(工作流程将完成的文件保存到数据存储中)。

每个类的映射似乎更容易测试和分解。

Now that I've written a few of the transformations, I'm leaning towards the mapping-per-class approach,. I have to be able to run unit tests against faked models to ensure the files are built properly (testing them against known good sample files).

I've been making the mapping-delegates private to the "workflow" class they belong in (one "workflow" per model/file). I'd have to make them public to unit test them, or output the intermediate file content (the workflow saves the completed files to a data store).

The mapping-per-class seems a lot more testable and decomposable.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文