动态映射输入文件的C#方法

动态映射输入文件的C#方法,c#,orm,C#,Orm,我试图找出将输入文件(如XLS或CSV文件)映射到系统中对象的最佳方法。让我再详细说明一下。我有以下目标: 公司 接触 每个变量都有如下变量: 地址 电话号码 电子邮件 等 我能得到的输入文件是不同的,有时会有列标题,有时不会,列的排列和编号会不时改变。它可以如下所示: COMPANY - CONTACT - ADDRESS - PHONE ----------------------------------------------------------

我试图找出将输入文件(如XLS或CSV文件)映射到系统中对象的最佳方法。让我再详细说明一下。我有以下目标:

公司 接触

每个变量都有如下变量:

地址 电话号码 电子邮件 等

我能得到的输入文件是不同的,有时会有列标题,有时不会,列的排列和编号会不时改变。它可以如下所示:

COMPANY         - CONTACT       - ADDRESS       - PHONE
----------------------------------------------------------------------
company1          contact1        address1        phone1
company1          contact2        address2
company2          contact3                        phone2
                  contact4        address3
上面显示“company”将“addres1”和“phone1”附加到company,“contact1”是它自己的对象,但“company1”是它的父对象

“contact2”也是如此(手机除外)

“contact4”没有父对象,因此“address3”属于联系人,而不是公司

到目前为止,我的想法是实现以下目标:

映射-(这是我不确定如何实现它的地方。它应该说明列应该如何映射到变量、所有权/层次结构-例如,公司有地址)

IMappingLoader-(加载映射对象)

--XmlMappingLoader

--DbMappingLoader

IDataLoader-(将数据加载到数据集中)

--XLSLoader

--CSV装载机

因此将加载映射,将数据加载到数据集中,并返回正确的对象

我不确定最好的方法是如何进行映射。如何能够说哪列应该属于哪一个对象

谢谢你的建议


Jon

您的解析器必须知道您的列……否则它无法将数据映射到特定的对象属性。当然,除非引入一个索引属性类,该类可以根据信息的读取顺序存储信息

您应该创建一个解析器工厂,并根据文件的扩展名返回作业的正确解析器,例如

public class Record 
{ 
    private Dictionary<int, string> items = new Dictionary<int, string>(); 
    private int propCount; 

    public Record(int size) 
    { 
        // populate array with empty strings 
        for(int i = 0; i <= size -1; i++) 
            items.Add(i, String.Empty); 
        propCount = size; 
    } 

    public string this[int index] 
    { 
        get { return items[index]; } 
        set { items[index] = value; } 
    } 

    public int PropertyCount { get { return propCount; } } 
}

public interface IRecordParser 
{ 
    string FileName { get; set; } 
    string[] GetHeadings(); 
    bool HasHeaders { get; set; } 
    void GoToStart(); 
    Record ParseNextRecord(); 
} 

public abstract class RecordParser 
{ 
    public string FileName { get; set; } 
    public bool HasHeaders { get; set; } 
    public abstract string[] GetHeadings(); 
    public abstract void GoToStart(); 
    public abstract Record ParseNextRecord();     
} 

public class ExcelRecordParser : RecordParser, IRecordParser 
{ 
    public ExcelRecordParser() 
    { 
    } 

    public override string[] GetHeadings() 
    { 
        if (HasHeaders)  
            // return column headings
        else 
            // return default headings from settings file
    } 

    public override void GoToStart() 
    { 
        // navigate to first row (or +1 if HasHeaders is true) 
    } 

    public override Record ParseNextRecord() 
    { 
        var headers = GetHeadings(); 
        var r = new Record(headers.Length);

        // enumerate rows, then for each row do...
        for(int i = 0; i <= headers.Length - 1; i++) 
            r[i] = row[i];

        return r;
    } 
}

public class CsvRecordParser : RecordParser, IRecordParser 
{ 
    public CsvRecordParser() 
    { 
    } 

    public override string[] GetHeadings() 
    { 
        if (HasHeaders) 
            // return first row split as headings
        else 
            // return default headers from settings file
    } 

    public override void GoToStart() 
    { 
        // navigate to start of file (or +1 if HasHeaders is true) 
    } 

    public override Record ParseNextRecord() 
    { 
        var headers = GetHeadings(); 
        var r = new Record(headers.Length);

        // enumerate lines, then for each line do...
        for(int i = 0; i <= headers.Length - 1; i++) 
            r[i] = line[i];

        return r;
    } 
} 

public static class RecordParserFactory 
{ 
    public static IRecordParser Create(string ext) 
    { 
        switch (ext) 
        { 
            case ".xls": 
                return new ExcelRecordParser() as IRecordParser; 
            case ".csv": 
                return new CsvRecordParser() as IRecordParser;
            default:
                return null;
        } 
    } 
}

如果您的文件格式将来发生变化,例如XML、二进制等,这将允许您添加其他解析器

嗨,詹姆斯,是的,这是我很满意的部分,并且实现了非常类似的功能。我想我一直试图间接地把事情复杂化。我想让业务部门通过设置列并自己映射来确保动态加载新数据。但是业务逻辑可以/不应该改变,因此我认为您已经为我找到了一个好的解决方案。谢谢:)@Jon,很乐意帮忙。解析动态信息总是很困难的,但是,我要说的是,您的系统确实需要至少了解一些有关它正在解析的数据的信息,否则它将无法映射它。当然,除非完全取决于数据的存储顺序,否则在这种情况下,您可能会使用反射甚至更新类来支持索引属性。我将更新我的答案,向你们展示我的意思。@Jon,请看我更新的答案。它现在包括一个基于索引属性的类。基本上,这允许您拥有所需的任意多个属性,并且它将始终基于信息的顺序进行填充。您可以很容易地更新Record类,以获取标题列表而不是大小,然后根据标题获取信息。不过,为了尽可能地通用,请坚持使用索引。谢谢James。确实很有帮助。非常感谢。:)这确实澄清/巩固了我的想法并确定了方法。
// would return an instance of CSV Parser
string file = @"C:\Data\MyRecords.csv";
IRecordParser parser = RecordParserFactory.Create(System.IO.Path.GetExtension(file));

// would return an instance of Excel Parser
string file = @"C:\Data\MyRecords.xls";
IRecordParser parser = RecordParserFactory.Create(System.IO.Path.GetExtension(file));