2016-04-12 1 views
1

さまざまな種類のCSVファイルを読み込むクラスを作成しています。 Modelクラスに基づいて重要な情報を取り出します。ここでモデルクラスのプロパティは取得したい列名です。たとえば、列FromAddressとToAddressを持つOutlookModelを持つことができます。あるいは、全く異なる列を持つSalesforceModelを持つことができます。リストタイプを動的に作成する(または別のコレクションですか?)

リーダークラスが行と列を解析すると、モデルクラスのインスタンスにセルが読み込まれます。以下のコードでは、引数className = OutlookModelです。ここで最も関連性の高いコード行は、署名と返品です。

protected void MapColumns(string row, string className, List<OutlookModel> list) 
    { 
     string[] cols = row.Split(','); 
     // create a model to save the important columns 
     var model = Activator.CreateInstance(nameSpace, nameSpace + className); 
     int j = 0; 
     if (cols.Length > 0) 
     { 
      foreach (var c in cols) 
      { 
       // is this column index one of our important columns? 
       if (Ordinals.ContainsKey(j)) 
       { 
        // this is a column we care about, so set the model property 
        model.GetType().GetProperty(Ordinals[j]).SetValue(model, c); 
       } 
       j++; 
      } 
     } 
     list.Add(model); 
    } 

私が抱えている問題は、モデルオブジェクトのコレクションです。引数にオブジェクトをList < OutlookModel>として定義すると、このメソッドは拡張できません。リスト<オブジェクト>として定義すると、モデル間ですべて異なるプロパティを使用するために内部リストをキャストする必要があります。

私はC#のかなり新しいです。これらの異なるモデルタイプをリスト/配列/コレクション/に取り込むより良い方法はありますか?それで、リストにロジックを適用できますか?

+0

実行時に 'Ordinals'が含まれているものは何ですか?このメソッドを呼び出す前に値を変更しますか? – thehennyy

+0

オーディナルは、Excelファイルのヘッダーの列インデックスを含む辞書です。 Excelファイルのヘッダーはモデルのプロパティと一致し、それが辞書の値になります。たとえば、3番目の列が「FromAddress」の場合、Ordinals [2] = FromAddressになります。 Ordinalsディクショナリには、Excelファイルの最初の行がロードされます。このMapColumnsメソッドは、後続の各行に対して実行されます。 – tugowar

+0

このメソッドのシグネチャを使用する必要がありますか? – thehennyy

答えて

1

まず最初に、カスタム属性を追加して、csvから読み込みたいプロパティをマークすることをお勧めします。後で何かを追加しなければならないときに実行しないでください。あまりにも多くの魔法の弦に頼っている。ここに私のテストのセットアップは次のとおりです。

class ReadFromCsvAttribute : Attribute { } 

    class OutlookModel 
    { 
     public int DontSetThisValueFromCsv { get; set; } 

     [ReadFromCsv] 
     public string FromAddress { get; set; } 

     [ReadFromCsv] 
     public string ToAddress { get; set; } 
    } 

    class SalesForceModel 
    { 
     [ReadFromCsv] 
     public string Name { get; set; } 

     [ReadFromCsv] 
     public string Age { get; set; } 
    } 

    static void Main(string[] args) 
    { 
     string outlookSample = "Id,FromAddress,ToAddress,Useless\r\n" + 
           "1,[email protected],[email protected],asdf\r\n" + 
           "3,[email protected],[email protected],baz"; 

     string salesForceSample = "Id,Name,Age\r\n" + 
            "1,John,30\r\n" + 
            "2,Doe,100"; 

     var outlook = ReadFromCsv<OutlookModel>(outlookSample); 

     var salesForce = ReadFromCsv<SalesForceModel>(salesForceSample); 

    } 

私はあなたがデータから好きなモデルを読み取るために、この一般的な方法まとめ:

static List<T> ReadFromCsv<T>(string data) 
{ 
    var objs = new List<T>(); 
    var rows = data.Split(new[] {"\r\n"}, StringSplitOptions.None); 

    //create index, header dict 
    var headers = rows[0].Split(',').Select((value, index) => new {value, index}) 
     .ToDictionary(pair => pair.index, pair => pair.value); 

    //get properties to find and cache them for the moment 
    var propertiesToFind = typeof (T).GetProperties().Where(x => x.GetCustomAttributes<ReadFromCsvAttribute>().Any()); 

    //create index, propertyinfo dict 
    var indexToPropertyDict = 
     headers.Where(kv => propertiesToFind.Select(x => x.Name).Contains(kv.Value)) 
      .ToDictionary(x => x.Key, x => propertiesToFind.Single(p => p.Name == x.Value)); 

    foreach (var row in rows.Skip(1)) 
    { 
     var obj = (T)Activator.CreateInstance(typeof(T)); 

     var cells = row.Split(','); 
     for (int i = 0; i < cells.Length; i++) 
     { 
      if (indexToPropertyDict.ContainsKey(i)) 
      { 
       //set data 
       indexToPropertyDict[i].SetValue(obj, cells[i]); 
      } 
     } 
     objs.Add(obj); 
    } 

    return objs; 
} 
+0

ありがとうございます。私はこれを分析するのに時間を費やす必要があります。それは私が今日知っているものとは非常に異なっています。私はそれを書いていただきありがとうございます。 – tugowar

1

ここで別のサンプルです。あなたはC#の初心者ですから、可能な限りlinqと拡張メソッドを避けました。それをコンソールアプリケーションにコピーして実行してください。

また、私は、あなたがあなたの生態系を完全にコントロールしている場合にのみ、クラスを記述するために.net属性を使用することをおすすめします。

public class Account 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
} 

public class LastNameAccount 
{ 
    public string LastName { get; set; } 
    public string Address { get; set; } 
} 

public class Program 
{ 
    public static void Main(string[] args) 
    { 
     Test1(); 
    } 

    private static void Test1() 
    { 
     /* 
     * defines the result of your CSV parsing. 
     */ 
     List<string> csvColumns = new List<string> { "FirstName", "LastName" }; 
     List<List<string>> csvRows = new List<List<string>>() { 
      new List<string>(){"John","Doe"}, 
      new List<string>(){"Bill", "Nie"} 
     }; 

     //Map the CSV files to Account type and output it 
     var accounts = Map<Account>(csvColumns, csvRows); 
     if (accounts != null) 
     { 
      foreach (var a in accounts) 
      { 
       Console.WriteLine("Account: {0} {1}", a.FirstName, a.LastName); 
      } 
     } 

     //Map the CSV files to LastNameAccount type and output it 
     var accounts2 = Map<LastNameAccount>(csvColumns, csvRows); 
     if (accounts2 != null) 
     { 
      foreach (var a in accounts2) 
      { 
       Console.WriteLine("Last Name Account: {0} {1}", a.LastName, a.Address); 
      } 
     } 
    } 


    private static List<T> Map<T>(List<string> columns, List<List<string>> rows) 
     where T : class, new() 
    { 
     //reflect the type once and get valid columns 
     Type typeT = typeof(T); 
     Dictionary<int, PropertyInfo> validColumns = new Dictionary<int, PropertyInfo>(); 
     for (int columnIndex = 0; columnIndex < columns.Count; columnIndex++) 
     { 
      var propertyInfo = typeT.GetProperty(columns[columnIndex]); 
      if (propertyInfo != null) 
      { 
       validColumns.Add(columnIndex, propertyInfo); 
      } 
     } 

     //start mapping to T 
     List<T> output = null; 
     if (validColumns.Count > 0) 
     { 
      output = new List<T>(); 
      foreach (var row in rows) 
      { 
       //create new T 
       var tempT = new T(); 

       //populate T's properties 
       foreach (var col in validColumns) 
       { 
        var propertyInfo = col.Value; 
        var columnIndex = col.Key; 

        propertyInfo.SetValue(tempT, row[columnIndex]); 
       } 

       //add it 
       output.Add(tempT); 
      } 
     } 

     return output; 
    } 
} 
+0

ありがとうございます! – tugowar

関連する問題