Andando di Reflection ho scritto quasi tutto impostando anche le variabili.
Condivido il codice in modo tale che possa essere di aiuto a qualcuno o se qualcuno in collaborazione vuole migliorarlo magari con conoscenze più avanzate delle mie:
Esempio html ma può essere un file txt o qualsiasi altra cosa come l'estrapolazione di un csv generico:
<!doctype html>
<html class="fixed">
<head>
<!-- Basic -->
<meta charset="UTF-8">
<!-- Mobile Metas -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<!-- Web Fonts -->
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700,800|Shadows+Into+Light" rel="stylesheet" type="text/css">
<!-- Vendor CSS -->
<link rel="stylesheet" href="bootstrap.css" />
<!-- Theme CSS -->
<link rel="stylesheet" href="theme.css" />
</head>
<body>
<section class="body">
@var text_success_marginalita_documento = Model.MarginalitaPositiva.ToString().Replace("False","text-danger").Replace("True","text-success")@
@var decimali_generici = Model.ConfigNumeroDecimali@
@var decimali_quantita = Model.ConfigNumeroDecimali@
@var decimali_prezzi = Model.ConfigNumeroDecimali@
@var decimali_sconti = Model.ConfigNumeroDecimali@
<div class="inner-wrapper full">
<section role="main" class="content-body full">
<!-- start: page -->
<section class="panel">
<div class="panel-body">
<div class="invoice">
<header class="clearfix">
<div class="row">
<div class="col-sm-12 mt-md mb-md">
<h2 class="h2 mt-none mb-sm text-dark text-weight-bold">DDT</h2>
<h4 class="h4 m-none text-dark text-weight-bold">N. @Model.TESTATA.NumeroDocumento.ToString()@</h4>
<h4 class="h4 m-none text-dark text-weight-bold">del @Model.TESTATA.DataDocumento.ToString("dd/MM/yyyy")@</h4>
</div>
</div>
</header>
<div class="bill-info">
<div class="row">
<div class="col-md-6">
<div class="bill-to">
<p class="h5 mb-xs text-dark text-weight-semibold">Intestazione:</p>
<address>
@Model.INTESTAZIONE.Descrizione@
<br/>
(@Model.INTESTAZIONE.CODICE@)
<br />
@Model.INTESTAZIONE.Indirizzo@ @Model.INTESTAZIONE.Localita@ (@Model.INTESTAZIONE.Provincia@) - @Model.INTESTAZIONE.Cap@
</address>
</div>
</div>
<div class="col-md-6">
<div class="bill-to text-right">
<p class="h5 mb-xs text-dark text-weight-semibold">Destinazione:</p>
<address>
@Model.DESTINAZIONE.Descrizione@
<br />
(@Model.DESTINAZIONE.CODICE@)
<br />
@Model.DESTINAZIONE.Indirizzo@ @Model.DESTINAZIONE.Localita@ (@Model.DESTINAZIONE.Provincia@) - @Model.DESTINAZIONE.Cap@
</address>
</div>
</div>
</div>
</div>
<div class="table table-no-more table-bordered table-striped mb-none">
<table class="table invoice-items">
<thead>
<tr class="text-dark">
<th class="hidden-xs hidden-sm text-right" style="width:40px">N</th>
<th class="text-weight-semibold">Codice</th>
<th class="text-weight-semibold">Descrizione</th>
<th class="text-center text-weight-semibold">C</th>
<th class="text-left text-weight-semibold">Lotto</th>
<th class="text-right text-weight-semibold">Qta</th>
<th class="text-right text-weight-semibold">Qta Ulte</th>
<th class="text-right text-weight-semibold">Prezzo</th>
<th class="text-right text-weight-semibold">Sconto</th>
<th class="text-right text-weight-semibold">Totale Riga</th>
<th class="text-center text-weight-semibold">Marginalità</th>
</tr>
</thead>
<tbody>
@foreach Model.RIGHE into item@
@fvar label_success_marginalita_documento = Model.MarginalitaPositiva.ToString().Replace("False","label-danger").Replace("True","label-success")@
<tr>
<td data-title="N" class="text-right text-weight-semibold text-dark" style="width:40px">1</td>
<td data-title="Codice" class="text-weight-semibold text-dark">@item.GetDocRigBase.CodiceArticolo@</td>
<td data-title="Descrizione">@item.GetDocRigBase.Descrizione@</td>
<td data-title="C" class="text-center">@item.GetDocRigBase.Causale@</td>
<td data-title="Lotto" class="text-left">@item.GetDocRigBase.NumeroRiga@</td>
<td data-title="Qta" class="text-right">@item.GetDocRigBase.UnitaMisura@ @item.GetDocRigBase.Quantita.ToString("N@decimali_quantita")@</td>
<td data-title="Qta Ulte" class="text-right">@item.GetDocRigBase.UnitaMisura_1@ @item.GetDocRigBase.QuantitaUlteriore.ToString("N@decimali_quantita")@</td>
<td data-title="Prezzo" class="text-right">¤@item.GetDocRigBase.PrezzoUnitario.ToString("N@decimali_prezzi")@</td>
<td data-title="Sconto" class="text-right">@item.GetDocRigBase.Sconti@</td>
<td data-title="Totale Riga" class="text-right">¤ @item.TotaleRiga.ToString("N@decimali_generici")@</td>
<td data-title="Marginalità" class="text-center"><span class="label @label_success_marginalita_documento" style="font-size:13px;display:block"> @item.Marginalita.ToString("N@decimali_generici")@ (@item.PercentualeMarginalita.ToString("N@decimali_generici")@%)</span></td>
</tr>
@end
</tbody>
</table>
</div>
<div class="invoice-summary">
<div class="row">
<div class="col-sm-4 col-sm-offset-8">
<table class="table h5 text-dark">
<tbody>
<tr class="b-top-none">
<td colspan="2">Imponibile</td>
<td class="text-right">@Model.TotaleImponibile.ToString("N@decimali_generici")@¤</td>
</tr>
<tr>
<td colspan="2">Imposta</td>
<td class="text-right">@Model.TotaleImposta.ToString("N@decimali_generici")@¤</td>
</tr>
<tr>
<td colspan="2">Omaggi</td>
<td class="text-right">@Model.TotaleOmaggi.ToString("N@decimali_generici")@¤</td>
</tr>
<tr>
<td colspan="2">Totale Documento</td>
<td class="text-right">@Model.TotaleDocumento.ToString("N@decimali_generici")@¤</td>
</tr>
<tr>
<td colspan="2">Acconto</td>
<td class="text-right">@Model.TESTATA.IncassoSuDocumento.ToString("N@decimali_generici")@¤</td>
</tr>
<tr class="h4">
<td colspan="2">Totale a Pagare</td>
<td class="text-right">@Model.NettoAPagare.ToString("N@decimali_generici")@¤</td>
</tr>
<tr class="h5">
<td colspan="2" class="@text_success_marginalita_documento text-weight-semibold">Marginalità</td>
<td class="text-right @text_success_marginalita_documento text-weight-semibold">@Model.TotaleMarginalita.ToString("N@decimali_generici")@¤ (@Model.PercentualeMarginalita.ToString("N@decimali_generici")@%)</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- end: page -->
</section>
</div>
</section>
</body>
</html>
Questa è la libreria completa che ho scritto fino ad ora:
class EvaluateCodeDynamic
{
public static string Eval<T>(string testo, T Classe)
{
Type t = typeof(T);
var ListDeclaringProperties = t.GetTypeInfo().DeclaredProperties;
//devo replicare tutte le variabili generiche
testo = ReplaceVar("@var",testo,Classe, ListDeclaringProperties.ToList());
//devo cercare tutti i tag che iniziano per e finiscono per con le regular expression
Regex reg = new Regex("(@Model.)(.*?)(@)");
var models = reg.Matches(testo).OfType<Match>().ToList();
foreach (var model in models)
{
var chiave = model.Groups[2].ToString().Trim();
var valore = GetPropertyInfo<T>(chiave, Classe, ListDeclaringProperties.ToList());
if (valore != null)
{
testo = testo.Replace(model.Groups[0].ToString(), valore.ToString());
}
}
//creo i tag per foreach
//@foreach e vinisce con @end
Regex reg_foreach = new Regex("(@foreach)(.*?)(@end)",RegexOptions.Singleline);
var m_match = reg_foreach.Matches(testo).OfType<Match>().ToList();
foreach (var match in m_match)
{
string text_foreach = match.Groups[2].ToString();
string text_foreach_new = ForeachInProperty(Classe, text_foreach, ListDeclaringProperties.ToList());
testo = testo.Replace(match.Groups[0].ToString(), text_foreach_new);
}
return testo;
}
private static object GetPropertyInfo<T>(string chiave, T Classe, List<PropertyInfo> PropertiesInfo)
{
object valore_finale = null;
string[] puntatori = chiave.Split('.');
var puntatore = puntatori[0];
var prop_puntatore = PropertiesInfo.FirstOrDefault(fi => fi.Name == puntatore);
if (prop_puntatore != null)
{
object valore_puntatore = prop_puntatore.GetValue(Classe, null);
if (valore_puntatore != null)
{
chiave = chiave.Replace(puntatore, "");
if (chiave.ToString().Trim().Length > 0)
{
chiave = chiave.Substring(1);
Type t = valore_puntatore.GetType();
if (!t.GetTypeInfo().ToString().StartsWith("System."))
{
var ListDeclaringProperties = t.GetTypeInfo().DeclaredProperties;
valore_finale = GetPropertyInfo(chiave, valore_puntatore, ListDeclaringProperties.ToList());
}
else
{
valore_finale = CreateMetothInfo(chiave, valore_puntatore);
}
}
else
{
valore_finale = valore_puntatore;
}
}
}
return valore_finale;
}
private static object CreateMetothInfo(string chiave, object valore)
{
string[] puntatori = chiave.Split('.');
foreach (var puntatore in puntatori)
{
Type t = valore.GetType();
string nome = !puntatore.Contains("(") ? puntatore : puntatore.Substring(0, puntatore.IndexOf("("));
string[] param = null;
int count_param = 0;
if (puntatore.Contains("("))
{
param = puntatore.Substring(puntatore.IndexOf("(") + 1).Replace("(", "").Replace(")", "").Replace("\"", "").Split(',');
if (param.Length == 1 && param[0] == "")
{
param = null;
}
else
{
count_param = param.Count();
}
}
MethodInfo metodo = null;
if (count_param == 0)
{
metodo = t.GetMethods().FirstOrDefault(f => f.Name.StartsWith(nome) && f.GetParameters().Count() == 0);
}
else
{
var metodi = t.GetMethods().ToList().FindAll(f => f.Name.StartsWith(nome) && f.GetParameters().Count() == count_param);
foreach (var prop_metodo in metodi)
{
bool trovato = true;
var parametri = prop_metodo.GetParameters();
for (int i = 0; i < parametri.Count(); i++)
{
if (parametri[i].ParameterType != param[i].GetType())
{
trovato = false;
break;
}
}
if (trovato)
{
metodo = prop_metodo;
}
}
}
if (metodo != null)
{
valore = metodo.Invoke(valore, param != null ? param.ToArray() : null);
}
}
return valore;
}
private static string ForeachInProperty<T>(T Classe,string text_foreach,List<PropertyInfo> PropertiesInfo)
{
Regex reg = new Regex("(Model.)(.*?)(@)");
var m_match = reg.Matches(text_foreach).OfType<Match>().ToList();
if (m_match.Count == 0)
{
return "";
}
var match = m_match[0];
string expression_into = match.Groups[2].ToString();
text_foreach = text_foreach.Replace(match.Groups[0].ToString(), "").Trim();
string item_property_iter_class = Regex.Split(expression_into, "into")[0].ToString().Trim();
string item_name_into_foreach ="@"+ Regex.Split(expression_into, "into")[1].ToString().Trim();
//text_foreach = testo da replicare con i valori per ogni riga
//item_name_into_foreach = nome item della classe che troverò nel testo text_item
//item_property_iter_class = nome della property nella classe sul quale dovrò iterare
var list_obj = GetPropertyInfo(item_property_iter_class, Classe, PropertiesInfo);
if (list_obj == null)
{
return "";
}
List<object> MyList = (list_obj as IEnumerable<object>).Cast<object>().ToList();
if (MyList.Count == 0)
{
return "";
}
string my_text_items = "";
foreach (var item in MyList)
{
string my_text_item = text_foreach;
Type t_item = item.GetType();
var ListDeclaringProperties_item = t_item.GetTypeInfo().DeclaredProperties;
my_text_item = ReplaceVar("@fvar", my_text_item, item, ListDeclaringProperties_item.ToList());
//devo cercare tutti i tag che iniziano per e finiscono per con le regular expression
string text_reg = "(" + item_name_into_foreach + ".)(.*?)(@)";
reg = new Regex(text_reg);
m_match = reg.Matches(text_foreach).OfType<Match>().ToList();
foreach (var model in m_match)
{
var chiave = model.Groups[2].ToString().Trim();
var valore = GetPropertyInfo(chiave, item, ListDeclaringProperties_item.ToList());
if (valore != null)
{
my_text_item = my_text_item.Replace(model.Groups[0].ToString(), valore.ToString());
}
}
my_text_items += my_text_item;
}
return my_text_items;
}
private static string ReplaceVar<T>(string tag_var, string testo,T Classe, List<PropertyInfo> PropertiesInfo)
{
Regex reg = new Regex("("+tag_var+")(.*?)(@)");
var m_match = reg.Matches(testo).OfType<Match>().ToList();
foreach (var match in m_match)
{
string variabile = match.Groups[2].ToString();
testo = testo.Replace(match.Groups[0].ToString(), "");
string nome_variabile = "@" + variabile.Split('=')[0].Trim();
string valore_variabile = variabile.Split('=')[1].Trim();
if (valore_variabile.Contains("Model."))
{
valore_variabile = GetPropertyInfo<T>(valore_variabile.Replace("Model.",""), Classe, PropertiesInfo).ToString();
}
testo = testo.Replace(nome_variabile, valore_variabile);
}
return testo;
}
}
gestisce foreach, impostazione di variabili e qualsiasi metodo System su una property e quindi ad esempio:
ToString()
ToString("format data o numero")
Replace()
Devo dire che esistono librerie Evaluate come ad esempio
https://github.com/zzzprojects/Eval-Expression.NET
che potrebbero consentire di esprimere il valore ma richiede troppe librerie che potrebbero andare in conflitto con un progetto completo su Android, iOS e UWP: bisogna mantenere sempre la compatibilità.
Per il momento vado su questa strada perchè mi consente di poter aggiungere e di far aggiungere a terze parti dei file senza strumenti di sviluppo e farli eseguire all'interno dell'applicazione.