J'ai une table avec des nombres décimaux (et d'autres types) enregistrés en tant que chaînes. Je souhaite écrire une requête Linq sur le contexte de la base de données, qui sera traduite en une conversion de base de données et non en une distribution locale (pour des raisons de performances).
Cet exemple de non-travail est conceptuellement ce que je veux réaliser.
using ( var context = new MyContext() )
{
return context.SomeTable
.Select(o => new { o.Id, (decimal)o.SomeString });
}
C’est un mauvais moyen de le réaliser car il exécutera la conversion du côté de l’application.
using ( var context = new MyContext() )
{
return context.SomeTable
.Select(o => new { o.Id, o.SomeString })
.ToList()
.Select(o => new { o.Id, Convert.ToDecimal(o.SomeString) });
}
Je pense que la solution consiste à utiliser DbFunctions, mais je ne trouve pas le moyen de l'utiliser avec Code First.
C'est la réponse partielle, mais je n'ai pas été en mesure de trouver la documentation nécessaire pour finaliser la partie où je définis ce que cette fonction fait sur le serveur SQL.
[DbFunction("MyContext", "ConvertToDecimal")]
public static decimal ConvertToDecimal(string s)
{
throw new Exception("Direct calls are not supported.");
}
.
using ( var context = new MyContext() )
{
return context.SomeTable
.Select(o => new { o.Id, ConvertToDecimal(o.SomeString) });
}
Si j'utilisais une alternative pilotée par Edmx , ce serait la partie manquante:
<Function Name="ConvertToDecimal" ReturnType="Edm.Decimal">
<Parameter Name="s" Type="Edm.String" />
<DefiningExpression>
CAST(s AS decimal(22,6))
</DefiningExpression>
</Function>
J'utilise Entity Framework 6 Code First.
J'ai trouvé la solution avec quelques informations provenant de ce sujet.
Comme je le supposais dans la question initiale, j'avais les deux premières parties. La dernière partie consiste à enregistrer dans DbModel les fonctions que vous souhaitez rendre accessibles et leur utilisation. Il y a plus d'une façon de faire cela mais j'ai utilisé une convention
public class MyFunctionsConvetion : IStoreModelConvention<EntityContainer>
{
public void Apply(EntityContainer item, DbModel model)
{
//Get the Edm Model from the DbModel
EdmModel storeModel = model.GetStoreModel();
//Delare your parameters name, edm type and mode (You can ignore this if you use a parameter-less function)
List<FunctionParameter> Parameters = new List<FunctionParameter>();
Parameters.Add(FunctionParameter.Create("StringValue", GetStorePrimitiveType(model, PrimitiveTypeKind.String), ParameterMode.In));
//Same thing goes for the return type(s) (Why is it a list? Perhaps you can return tables? I haven't tested however since it is no use to me)
List<FunctionParameter> ReturnParameters = new List<FunctionParameter>();
ReturnParameters.Add(FunctionParameter.Create("ReturnValue", GetStorePrimitiveType(model, PrimitiveTypeKind.Decimal), ParameterMode.ReturnValue));
//Create the payload and fill the required information alone with the parameter lists we declared
EdmFunctionPayload payload = new EdmFunctionPayload();
payload.IsComposable = true;
payload.Schema = "dbo";
payload.StoreFunctionName = "ConvertToDecimal";
payload.ReturnParameters = ReturnParameters;
payload.Parameters = Parameters;
//Create the function with it's payload
EdmFunction function = EdmFunction.Create("ConvertToDecimal", "MyContext", DataSpace.SSpace, payload, new MetadataProperty[] { });
//Add it to the model
storeModel.AddItem(function);
}
//Little helper method to get the primitive type based on the database provider
private EdmType GetStorePrimitiveType(DbModel model, PrimitiveTypeKind typeKind)
{
return model
.ProviderManifest
.GetStoreType(TypeUsage.CreateDefaultTypeUsage(PrimitiveType.GetEdmPrimitiveType(typeKind)))
.EdmType;
}
}
Ensuite, nous ajoutons la convention au modèle dans la méthode OnModelCreating
modelBuilder.Conventions.Add<MyProject.MyConventions.MyFunctionsConvention>();
Remarque: le code pourrait être plus propre et écrit de manière SÈCHE, mais par souci de simplicité, je voulais le poster comme ceci et vous permettre de l'organiser comme bon vous semble.