LINQ II - přetypovávání i vnořených anonymních datových typů z jiné assembly
V předchozím spotu jsem byl schopen pracovat s anonymními datovými typy, i když byly dotazy a výsledné sady dat vytvořeny v jiné assembly. Odstranění vrozené xenofobie v praxi.:)
Náš kód ale vygeneruje výjimku, jestliže anonymní datový typ z jiné assembly obsahuje další vnořené anonymní datové typy jako v následujícím upraveném příkladu. Vlastnost InnerAT vrací další anonymní datový typ, který pro zajímavost obsahuje odkaz ještě na další anonymní datový typ.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LINQTEST
{
public class TestAT
{
public static object GetResult()
{
string[] rows = { "Toyota", "Lexus", "Audi" };
var test = from row in rows
select new
{
FirstLetter = row[0],
Index = 110,
Original = row,
InnerAT = new { X = row[1], B = new {A=1}}
};
return test;
}
}
}
Řešení spočívá v úpravě extenzí a to tak, že přidáme privátní metodu GetTypeInstance a přeneseme do ní většinu kódu z extenze ToAnonymousType. Metoda GetTypeInstance při neshodě datového typu očekávaného parametrem "našeho - v naší assembly dostupného" konstruktoru anonymního datového typu a datového typu vlastnosti anonymního datového typu z "cizí" assembly rekurzivně přenese data z "cizího" anonymního datového typu do "našeho".
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Reflection;
using System.Collections;
using LINQTEST;
namespace LINQAnonymous
{
/// <summary>
/// Rozšíření pro LINQ
/// </summary>
static class RSLinqExtensions
{
/// <summary>
/// Metoda přetypuje objekt na anonymní typ, jehož struktura byla předána v parametru <paramref name="prototype"/>
/// </summary>
/// <typeparam name="T">Kompilátorem odvozený anonymní typ</typeparam>
/// <param name="prototype">Prototyp se strukturou anonymního typu</param>
/// <returns>Instanci anonymního typu, nebo null, jestliže konverzi nelze provést</returns>
/// <remarks>Metoda se pokusí převést data z různých assembly</remarks>
public static T ToAnonymousType<T>(this object obj, T prototype)
where T: class
{
T atiObj = obj as T;
if (atiObj == null)
{
atiObj = GetTypeInstance(obj, prototype.GetType()) as T;
}
return (atiObj);
}
private static object GetTypeInstance(object obj, Type expected)
{
object atiObj = null;
ConstructorInfo constructorInfo = expected.GetConstructors()[0];
if (constructorInfo == null)
{
return null;
}
ParameterInfo[] paramInfos = constructorInfo.GetParameters();
PropertyInfo[] origProperties = obj.GetType().GetProperties();
if (paramInfos.Count() != origProperties.Count())
{
return null;
}
object[] paramArgs = new object[paramInfos.Count()];
for (int i = 0; i < paramArgs.Length; i++)
{
PropertyInfo origProperty = origProperties.Where(prop => prop.Name == paramInfos[i].Name).FirstOrDefault();
if (origProperty == null)
{
return null;
}
object val = origProperty.GetValue(obj, null);
if (origProperty.PropertyType != paramInfos[i].ParameterType)
{
val = GetTypeInstance(val, paramInfos[i].ParameterType);
}
paramArgs[i] = val;
}
atiObj = constructorInfo.Invoke(paramArgs);
return atiObj;
}
/// <summary>
/// Metoda vrátí
/// </summary>
/// <typeparam name="T">Kompilátorem odvozený anonymní typ</typeparam>
/// <param name="prototype">Prototyp se strukturou anonymního typu</param>
/// <returns>List instancí anonymního typu, nebo null, jestliže konverzi nelze provést</returns>
/// <remarks>Metoda se pokusí převést data z různých assembly</remarks>
public static List<T> CastToList<T>(this object obj, T prototype)
where T : class
{
List<T> list = new List<T>();
IEnumerable<T> enumerable = obj as IEnumerable<T>;
if (enumerable != null)
{
list.AddRange(enumerable);
}
else
{
IEnumerable enumObjects = obj as IEnumerable;
if (enumObjects == null)
{
return null;
}
foreach (object enumObject in enumObjects)
{
T currObject = ToAnonymousType(enumObject, prototype);
if (currObject == null)
{
//K čistění listu by neměl být důvod, ale garantujeme, že nevrátíme částečně naplněný list
list.Clear();
return list;
}
list.Add(currObject);
}
}
return list;
}
}
Při přetypovávání stačí stále jen zadat prototyp anonymního datové typu.
//Anonymní typ z jiné assembly!
var result2 = TestAT.GetResult().CastToList(new {FirstLetter = default(char),
Index =default(int),
Original = default(string),
InnerAT = new { X = default(char), B = new { A = default(int) } }
})
;
foreach (var res in result2)
{
Console.WriteLine(res.FirstLetter);
Console.WriteLine(res.Original);
}
Console.WriteLine(TestAT.
GetResult().
CastToList(new
{
FirstLetter = default(char),
Index = default(int),
Original = default(string),
InnerAT = new { X = default(char), B = new { A =default(int)} }
}
).
Where(car => car.FirstLetter == 'T')
.FirstOrDefault()
.ToString());
Console.ReadLine();
Friday, 09 May 2008 09:09:26 (Central Europe Standard Time, UTC+01:00)
.NET Framework | ASP.NET | Compact .Net Framework | LINQ | Windows Forms