我有一个维护应用程序,它必须将企业数据(来自各种数据库/表)转换为平面文件,每个文件都采用特定格式,供遗留应用程序使用。我有像这样的数据模型
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<T>
和工厂一起工作更舒服。(同样,仅在必要时)。
我假设一些基类很有用,因为StringBuilder
循环和Byte[]
编码很简单。具体类是否应该在基类中设置委托属性(而不是调用抽象方法)?我应该在方法上保留类型参数(以及这将如何影响基类/具体类)?
我准备好了任何解决方案。我的主要目标是易于维护。我现在有 12 个模型/文件,这可能会增加到 21 个。我可能需要在任何文件中插入任意页眉/页脚行(这就是我喜欢可覆盖的基类方法 Map 的原因)。