Как я могу наложить строку на десятичную с помощью пользовательской функции DbFunction

c# entity-framework entity-framework-6 linq

Вопрос

У меня есть таблица с десятичными знаками (и другими типами), сохраненными как строки. Я хочу написать запрос Linq в контексте базы данных, который будет транслироваться в базу данных, а не локальный (по соображениям производительности).

Этот нерабочий пример концептуально, чего я хочу достичь.

using ( var context = new MyContext() )
{
    return context.SomeTable
        .Select(o => new { o.Id, (decimal)o.SomeString });
}

Это плохой способ добиться этого, поскольку он будет запускать преобразование на стороне приложения.

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) });
}

Я считаю, что путь к DbFunctions, но я не могу найти способ использовать его с Code First.

Это частичный ответ, но я не смог найти документацию, необходимую для завершения части, где я определяю, что эта функция делает на сервере 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) });
}

Если бы я использовал альтернативу Edmx , это была бы недостающая часть:

<Function Name="ConvertToDecimal" ReturnType="Edm.Decimal">
    <Parameter Name="s" Type="Edm.String" />
    <DefiningExpression>
        CAST(s AS decimal(22,6))
    </DefiningExpression>
</Function>

Я использую Entity Framework 6 Code First.

Принятый ответ

Я выяснил решение с некоторой информацией, исходящей из этой темы.

Как я предполагал в исходном вопросе, у меня были первые 2 части. Последняя часть - зарегистрировать в DbModel функции, которые вы хотите получить, и как их использовать. Существует более чем один способ сделать это, но я использовал Конвенцию

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;
    }
}

Затем мы добавляем Конвенцию к модели в методе OnModelCreating

modelBuilder.Conventions.Add<MyProject.MyConventions.MyFunctionsConvention>();

Примечание. Код может быть более чистым и написан сухим способом, но для простоты я хотел бы опубликовать его так и позволить вам организовать его, как вы считаете нужным.



Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Является ли этот КБ законным? Да, узнайте, почему
Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Является ли этот КБ законным? Да, узнайте, почему