Sto lavorando con un database di terze parti in cui tutti i valori di testo sono memorizzati come char(n)
. Alcuni di questi valori di testo sono chiavi primarie, mentre altri sono solo normali testi leggibili dall'uomo. Per quest'ultimo, voglio che i valori recuperati vengano automaticamente ritagliati.
So che posso aggiungere Trim
a tutte le mie query LINQ su Entities, ma questo è disordinato, inaffidabile e non mantenibile. Mi piacerebbe in qualche modo configurare Entity Framework per tagliare automaticamente i valori recuperati da colonne specifiche.
Tuttavia, non so come farlo. Sto usando l'API fluente di EF. La cosa più vicina a cui ho pensato finora è la creazione di proprietà aggiuntive per avvolgere le proprietà reali con le chiamate al metodo Trim
, ma questo è disordinato e non è ancora molto mantenibile. Preferirei anche che il ritaglio si verificasse nel database piuttosto che nell'applicazione.
Rowan Miller (program manager di Entity Framework di Microsoft) ha recentemente pubblicato una buona soluzione a questo che utilizza Interceptors. Ammettiamo che questo è valido solo in EF 6.1+. Il suo post riguarda le stringhe finali dei join, ma in pratica la soluzione applicata rimuove ordinatamente le stringhe finali da tutte le proprietà delle stringhe nei modelli, automaticamente, senza influire in modo sensibile sulle prestazioni.
Post del blog originale: aggirare il problema degli spazi vuoti finali nei join di stringhe
Il codice pertinente è ripubblicato qui, ma ti incoraggio a leggere il suo post sul blog. (Anche se usi EF, dovresti comunque leggere il suo blog).
using System.Data.Entity.Core.Common.CommandTrees;
using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure.Interception;
using System.Linq;
namespace FixedLengthDemo
{
public class StringTrimmerInterceptor : IDbCommandTreeInterceptor
{
public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
{
if (interceptionContext.OriginalResult.DataSpace == DataSpace.SSpace)
{
var queryCommand = interceptionContext.Result as DbQueryCommandTree;
if (queryCommand != null)
{
var newQuery = queryCommand.Query.Accept(new StringTrimmerQueryVisitor());
interceptionContext.Result = new DbQueryCommandTree(
queryCommand.MetadataWorkspace,
queryCommand.DataSpace,
newQuery);
}
}
}
private class StringTrimmerQueryVisitor : DefaultExpressionVisitor
{
private static readonly string[] _typesToTrim = { "nvarchar", "varchar", "char", "nchar" };
public override DbExpression Visit(DbNewInstanceExpression expression)
{
var arguments = expression.Arguments.Select(a =>
{
var propertyArg = a as DbPropertyExpression;
if (propertyArg != null && _typesToTrim.Contains(propertyArg.Property.TypeUsage.EdmType.Name))
{
return EdmFunctions.Trim(a);
}
return a;
});
return DbExpressionBuilder.New(expression.ResultType, arguments);
}
}
}
}
Rowan continua: "Ora che abbiamo un intercettore, dobbiamo dire a EF di usarlo, questo è il modo migliore di farlo tramite la configurazione basata su codice. Possiamo semplicemente abbandonare la classe seguente nello stesso assembly / progetto del nostro contesto e EF sceglierà su. "
using System.Data.Entity;
namespace FixedLengthDemo
{
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
AddInterceptor(new StringTrimmerInterceptor());
}
}
}