Ho sviluppato un helper MVC per generare display e tabelle modificabili (è necessario un plug-in jquery per consentire l'aggiunta e l'eliminazione dinamiche di righe con postback completo nelle tabelle modificabili), ad es.Ottieni tipi utilizzando IEnumerable GetGenericArguments
@Htm.TableDisplayFor(m => m.MyCollection as ICollection)
che utilizzato in combinazione con gli attributi includerà totali nel piè, aggiungere colonne per visualizzare e modificare collegamenti, render collegamenti ipertestuali per tipo complesso ecc esempio
[TableColumn(IncludeTotals = true)]
Sto per pubblicarlo su CodeProject ma prima di farlo vorrei risolvere un problema. L'helper prima ottiene il ModelMetadata
dall'espressione, verifica che implementa ICollection
, poi si fa il tipo della collezione (si noti il seguente frammento è da Accettate le risposte su così, ma come spiegato di seguito, non è del tutto corretto)
if (collection.GetType().IsGenericType)
{
Type type = collection.GetType().GetGenericArguments()[0]
Il tipo viene utilizzato per generare ModelMetadata
per l'intestazione della tabella (potrebbero non esserci righe nella tabella) e ogni riga nel corpo della tabella (nel caso in cui alcuni elementi siano tipi ereditati che hanno proprietà aggiuntive e altrimenti rovinerebbero il layout colonna)
foreach (var item in collection)
{
ModelMetadata itemMetadata = ModelMetadataProviders.Current
.GetMetadataForType(() => item, type);
Quello che vorrei essere in grado di fare è utilizzare IEnumerable
anziché ICollection
in modo che non sia necessario chiamare .ToList()
sulle espressioni linq.
Nella maggior parte dei casi, IEnumerable
funziona correttamente, ad es.
IEnumerable items = MyCollection.Where(i => i....);
è OK perché .GetGenericArguments()
restituisce una matrice contenente un solo tipo. Il problema è che '.GetGenericArguments()' su alcune query restituisce 2 o più tipi e non sembra esserci alcun ordine logico. Ad esempio
IEnumerable items = MyCollection.OrderBy(i => i...);
restituisce [0] il tipo nella raccolta e [1] il tipo utilizzato per l'ordine.
In questo caso .GetGenericArguments()[0]
funziona ancora, ma
MyCollection.Select(i => new AnotherItem()
{
ID = i.ID,
Name = 1.Name
}
ritorna [0] il tipo della collezione originale e [1] il tipo di AnotherItem
Quindi .GetGenericArguments()[1]
è quello che mi serve per rendere il tabella per AnotherItem
.
La mia domanda è: esiste un modo affidabile che utilizza le istruzioni condizionali per ottenere il tipo di cui ho bisogno per eseguire il rendering della tabella?
Dai miei test fino ad ora, utilizzando .GetGenericArguments().Last()
funziona in tutti i casi tranne quando si utilizza OrderBy()
perché la chiave di ordinamento è l'ultimo tipo.
Un paio di cose che ho provato finora includere ignorando tipi che sono tipi di valore (come spesso il caso con OrderBy()
, ma OrderBy()
query potrebbe utilizzare un string
(che potrebbe essere verificata) o, peggio ancora, una classe che overload ==, < e> operatori (nel qual caso non sarei in grado di dire quale sia il tipo corretto), e non sono stato in grado di trovare un modo per verificare se la collezione implementa IOrderedEnumerable
.
S o, se capisco, tu feed _some_ 'IEnumerable' questo è il risultato (in genere) di qualche query _arbitrary_ LINQ. E vuoi ottenere in modo affidabile la parte 'T' di' IEnumerable '? –
@Chris Sì, anche se potrebbe essere solo IEnumerable (ad es. ArrayList) –
In tal caso, provare iterando sulle interfacce implementate, trovare l'interfaccia 'IEnumerable' ed estrarre la parte' T': 'var type = items.GetType() .GetInterfaces(). Where (t => t.IsGenericType) .Where (t => t.GetGenericTypeDefinition() == typeof (IEnumerable <>)). Single(). GetGenericArguments() [0]; 'Nota il mio uso di 'Single': se l'interfaccia non è implementata fallisce, o se implementa due (o più) diverse interfacce' IEnumerable ', allora fallisce. Puoi cambiarlo per usare 'FirstOrDefault' o' First' e gestirlo come preferisci per quei casi extra. –