2013-03-27 2 views
1

에게 연장을 사용하여 일반적인 유형의 리더를 만들려고 passing static reflection information to static generic methods이 질문에 반사

임 내가 클래스를 사용하여 데이터 액세스를 많이 할 같은 일반 유형 리더를 만들려고하고 난 매우 일반적인 방법을 만들려고 해요 많은 코드없이 데이터를 읽을 수 있습니다. 이

public static T Read<T>(string field,IDataRecord data) 
    { 
     Type type1 = typeof (T); 
     try 
     { 
      if (type1 == typeof(String)) 
      { 
       return (T)Convert.ChangeType(readString(data[field].ToString()), typeof(T)); 
      } 
      if (type1 == typeof(int?)) 
      { 
       return (T)Convert.ChangeType(readIntN(data[field].ToString()), typeof(T)); 
      } 
      if (type1 == typeof(Guid?)) 
      { 
       return (T)Convert.ChangeType(readGuidN(data[field].ToString()), typeof(T)); 
      } 
      if (type1 == typeof(double?)) 
      { 
       return (T)Convert.ChangeType(readDoubleN(data[field].ToString()), typeof(T)); 
      } 
      if (type1 == typeof(decimal?)) 
      { 
       var res = readDecimalN(data[field].ToString()); 
       return (T)Convert.ChangeType(res, typeof(T)); 
      } 
      if (type1 == typeof(float?)) 
      { 
       return (T)Convert.ChangeType(readFloatN(data[field].ToString()), typeof(T)); 
      } 
      if (type1 == typeof(bool?)) 
      { 
       return (T)Convert.ChangeType(readBoolN(data[field].ToString()), typeof(T)); 
      } 
      if (type1 == typeof(DateTime?)) 
      { 
       return (T)Convert.ChangeType(readDatetimeN(data[field].ToString()), typeof(T)); 
      } 
      if (type1 == typeof(int)) 
      { 
       return (T)Convert.ChangeType(readInt(data[field].ToString()), typeof(T)); 
      } 
      if (type1 == typeof(long?)) 
      { 
       return (T)Convert.ChangeType(readLongN(data[field].ToString()), typeof(T)); 
      } 
      if (type1 == typeof(long)) 
      { 
       return (T)Convert.ChangeType(readLong(data[field].ToString()), typeof(T)); 
      } 
      if (type1 == typeof(Guid)) 
      { 
       return (T)Convert.ChangeType(readGuid(data[field].ToString()), typeof(T)); 
      } 
      if (type1 == typeof(double)) 
      { 
       return (T)Convert.ChangeType(readDouble(data[field].ToString()), typeof(T)); 
      } 
      if (type1 == typeof(decimal)) 
      { 
       return (T)Convert.ChangeType(readDecimal(data[field].ToString()), typeof(T)); 
      } 
      if (type1 == typeof(float) || type1 == typeof(Single)) 
      { 
       return (T)Convert.ChangeType(readFloat(data[field].ToString()), typeof(T)); 
      } 
      if (type1 == typeof(bool)) 
      { 
       return (T)Convert.ChangeType(readBool(data[field].ToString()), typeof(T)); 
      } 
      if (type1 == typeof(DateTime)) 
      { 
       return (T)Convert.ChangeType(readDatetime(data[field].ToString()), typeof(T)); 
      } 
     } 
     catch (Exception) 
     { 
      throw; 
     } 
     throw new Exception(String.Format("Data Type Not Supported: {0}", type1)); 
    } 

그러나 이것은 잘못된 캐스트 예외를 던지고있다처럼

읽기의 대부분을 수행하는 코드의 일부가 보인다.

readXXX 방법

는 문제가 리턴 각 문에 발생하는되어 잘 작동하는

나는 또한

public static T SafeConvert<T>(string s, T defaultValue) 
{ 
if (string.IsNullOrEmpty(s)) 
    return defaultValue; 
return (T)Convert.ChangeType(s, typeof(T)); 
} 

를 사용하여 시도했지만 여전히 실패

편집 :

메소드 호출 중

private static List<T> GetItems<T>(IDataReader reader) 
    { 
     var items = new List<T>(); 
     while (reader.Read()) 
     { 
      Type type1 = typeof (T); 
      var item = (T) Activator.CreateInstance(typeof (T), new object[] {}); 
      foreach (PropertyInfo info in type1.GetProperties()) 
      { 
       int written = 0; 
       if (info.CanWrite) 
       { 
        #region 

        try 
        { 
         Type dataType = info.PropertyType; 
         MethodInfo method = typeof (DataReader).GetMethod("Read",BindingFlags.Static | BindingFlags.Public); 
         MethodInfo generic = method.MakeGenericMethod(dataType); 
         var t = generic.Invoke(null, new object[] {info.Name, reader}); 
         info.SetValue(item, t); 

더 많이 ...

사람들은 내가이를 사용할 것을 요구하는 것, 그리고 궁극적으로는 한 줄에있는 파일의 위치를 ​​전달하여이 CSV 또는 SQL 될 모든 소스에서 읽도록를 IEnumerable을 만들 날 수 있습니다 또는 SQL 쿼리 즉

//returns Ienumerable<MyClass> 
var list = Reader.ReadSql<MyClass>(DataBases.Test,"select * from TestTable where someAttribute = 1"); 
// also returns Ienumerable MyClass 
var list2 = Readre.ReadCsv<MyClass>(@"c:\file1.csv",","); 

내가 지금 실행해야하지만이 경우 데이터 유형 긴 목록의 repetiiton이 필요 == 대해서 typeof 내가 하나의 일반적인 읽기 방법으로 그 리팩토링 기대 된 각 구현 (문자열) 만입니다 변환에 문제가있다

+2

* 많은 * 코드를 게시했지만 메소드를 호출하는 방법이나 값이 무엇인지를 보여주지 않았습니다. 그것은 당신을 도우려는 것을 매우 어렵게 만듭니다. 또한 try/catch 블록은 무의미합니다. 원래 예외를 다시 던지면됩니다. –

+0

당신은 말하지 않았지만 IConvertable을 구현하지 않는 Guid와 같은 유형에서는 예외가 발생한다고 추측합니다. 이러한 유형의 생성자를 호출해야합니다. –

+0

안녕 존 전화가 상단에 링크로 이전 질문에서 만들어진, 나는이 질문에 대한 자세한 내용을 추가 할 것입니다 (예, 나는 잡기 시도가 현재 디버깅을 위해 불필요하다는 것을 알고 있습니다) – RoughPlace

답변

0

업데이트 된 퀘스트 n은 작동하지만 리팩터링하기를 원합니다. 글쎄, 할 수있어!

Dictionary<Type, Func<string, IDataRecord, object>> converters_ = new Dictionary<Type, Func<string, IDataRecord, object>>(); 

converters_[typeof(bool)] = (s) => { return readBoolN(data[s].ToString()); }; 
// repeat for other types: 

Then 

public static T Read<T>(string field,IDataRecord data) 
{ 
    return (T)converters_[typeof(T)](field, data); 
} 
+0

흥미 로움 나는이 감사를 들여다 보겠습니다. – RoughPlace

0
  1. 당신은 어떻게 제네릭의 예상을 얻을 수하는 Binder없이 GetMethod를 호출?

  2. Read<T>은 형식을 유추하는 데 사용할 수있는 인수를 사용할 수 있습니다. 형식 매개 변수를 사용하여 호출 할 때마다 필요합니까?

  3. 2가 예상 한 것일 경우 GetXXX 메서드를 호출하는 것과 다른 점은 무엇입니까?

나는 다음과 같은 코드를 원래의 설계를 따르지만, 고려 드릴 수 없습니다 :

public static partial class DataReaderExtensions { 
    /// <summary> 
    /// <para>Copy data to target object</para> 
    /// <para>Class which implements IDataRecord usually also implements IDataReader</para> 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="data"></param> 
    /// <param name="target"></param> 
    /// <returns>the count of field or property copied</returns> 
    public static int CopyTo<T>(this IDataRecord data, T target) { 
     return (
      from column in 
       Enumerable.Range(0, data.FieldCount).Select(
        (x, i) => new { 
         DataType=data.GetFieldType(i), 
         ColumnName=data.GetName(i) 
        } 
        ) 
      let type=target.GetType() 
      from member in type.GetMembers() 
      let typeMember= 
       member is PropertyInfo 
        ?(member as PropertyInfo).PropertyType 
        :member is FieldInfo 
         ?(member as FieldInfo).FieldType 
         :default(MemberInfo) 
      where typeMember==column.DataType 
      let name=member.Name 
      where name==column.ColumnName 
      let invokeAttr= 
       BindingFlags.SetProperty|BindingFlags.SetField| 
       BindingFlags.NonPublic|BindingFlags.Public| 
       BindingFlags.Instance 
      select type.InvokeMember(name, invokeAttr, default(Binder), target, new[] { data[name] }) 
      ).Count(); 
    } 
} 

당신은 문에 의해 사용자 정의 유형의 인스턴스에 직접 data를 복사합니다을 같은 같은 :

reader.CopyTo(myObject); 

공개/비공개 여부와 상관없이 필드 이름/속성과 함께 열 이름을 자동으로 매핑합니다. 마지막으로 복사 된 요소의 수를 반환합니다.