C# 在没有EDMX的LINQ语句中调用标量函数

C# 在没有EDMX的LINQ语句中调用标量函数,c#,sql,entity-framework,C#,Sql,Entity Framework,我正在尝试调用EF中的SQL标量函数。我尝试过各种各样的例子,但我不断得到: 无法将类型y上指定的方法x转换为LINQ 用于存储表达式的实体 我正在使用… 环境足迹6.1.3 EntityFramework.功能1.4.1 查询: 请注意,我正在选择中调用它 public IQueryable<MeterDataItem> Query() { var query = from meter in UnitOfWork.Meter join m

我正在尝试调用EF中的SQL标量函数。我尝试过各种各样的例子,但我不断得到:

无法将类型y上指定的方法x转换为LINQ 用于存储表达式的实体

我正在使用…

  • 环境足迹6.1.3
  • EntityFramework.功能1.4.1
查询:
请注意,我正在选择中调用它

public IQueryable<MeterDataItem> Query()
{
    var query = from meter in UnitOfWork.Meter
                join meterType in UnitOfWork.MeterType on meter.MeterTypeId equals meterType.Id into meterTypeLEFTJOIN
                    from meterType in meterTypeLEFTJOIN.DefaultIfEmpty()
                join company in UnitOfWork.Company on meter.CompanyId equals company.Id
                join meterPosition in UnitOfWork.EFMMeterPosition on meter.EFMMeterPositionId equals meterPosition.Id into meterPositionLEFTJOIN
                    from meterPosition in meterPositionLEFTJOIN.DefaultIfEmpty()
                join flowType in UnitOfWork.FlowType on meter.FlowTypeId equals flowType.Id into flowTypeLEFTJOIN
                    from flowType in flowTypeLEFTJOIN.DefaultIfEmpty()
                join fluidType in UnitOfWork.FluidType on meter.FluidTypeId equals fluidType.Id into fluidTypeLEFTJOIN
                    from fluidType in fluidTypeLEFTJOIN.DefaultIfEmpty()
                join runStatus in UnitOfWork.RunStatus on meter.RunStatusId equals runStatus.Id into runStatusLEFTJOIN
                    from runStatus in runStatusLEFTJOIN.DefaultIfEmpty()
                join pipeline in UnitOfWork.Pipeline on meter.PipelineId equals pipeline.Id into pipelineLEFTJOIN
                    from pipeline in pipelineLEFTJOIN.DefaultIfEmpty()

                // Device portion
                join device in UnitOfWork.Device on meter.DeviceId equals device.Id into deviceLEFTJOIN
                    from device in deviceLEFTJOIN.DefaultIfEmpty()
                join rtuDevice in UnitOfWork.RTUDevice on device.Id equals rtuDevice.DeviceId into rtuDeviceLEFTJOIN
                    from rtuDevice in rtuDeviceLEFTJOIN.DefaultIfEmpty()

                // Contact portion
                join measureTech in UnitOfWork.User on rtuDevice.MeasurementTechnicianId equals measureTech.Id into measureTechLEFTJOIN
                    from measureTech in measureTechLEFTJOIN.DefaultIfEmpty()
                join commTech in UnitOfWork.User on rtuDevice.CommunicationTechnicianId equals commTech.Id into commTechLEFTJOIN
                    from commTech in commTechLEFTJOIN.DefaultIfEmpty()

                // Connection portion
                join deviceCircuit in UnitOfWork.DeviceCircuit on device.Id equals deviceCircuit.DeviceId into deviceCircuitLEFTJOIN
                    from deviceCircuit in deviceCircuitLEFTJOIN.DefaultIfEmpty()
                join circuit in UnitOfWork.Circuit on deviceCircuit.CircuitId equals circuit.Id
                join circuitConnection in UnitOfWork.CircuitConnection on circuit.Id equals circuitConnection.CircuitId
                join connection in UnitOfWork.Connection on circuitConnection.ConnectionId equals connection.Id

                where
                    deviceCircuit.IsPrimary == true

                select new MeterDataItem()
                {
                    MeterId = meter.Id,
                    MeterNumber = meter.MeterNumber,
                    MeterName = meter.MeterName,
                    MeterTypeId = meterType.Id,
                    MeterTypeName = meterType.MeterTypeName,
                    MeterPositionCategory = meterPosition.EFMMeterPositionCategory,
                    FlowTypeName = flowType.FlowTypeName,
                    FluidTypeCategory = fluidType.FluidTypeCategory,
                    RunStatusCategory = runStatus.RunStatusCategory,
                    PipelineName = pipeline.PipelineName,
                    CompanyName = company.CompanyName,
                    ConnectionValue = GetConnection(circuitConnection.Id, connection.ConnectionTypeName),
                    DeviceId = device.Id,
                    DeviceName = device.DeviceName,
                    MeasurementTechnicianId = measureTech.Id,
                    MeasurementTechnicianFirstName = measureTech.FirstName,
                    MeasurementTechnicianLastName = measureTech.LastName,
                    CommunicationTechnicianId = commTech.Id,
                    CommunicationTechnicianFirstName = commTech.FirstName,
                    CommunicationTechnicianLastName = commTech.LastName,
                    MeterObjectStateName = null,    //<-- Default Value
                    FavoriteId = 0                  //<-- Default Value
                };

    return query.OrderBy(x => x.MeterNumber);
}
更新-回答:
我需要的一些更改包括:

  • 将函数类型更改为FunctionType.ComposableCalarValuedFunction
  • 将DbType参数字符串更改为小写
  • 确保在DbType声明中不包含大小:将VACHAR(50)更改为varchar
  • 将调用移动到具体的DbContext中
  • 在具体DbContext的OnModelCreating中注册FunctionConvention
具体的数据库上下文:
包括名称空间

using EntityFramework.Functions;
using StructureMap;
using System.Configuration;
using System.Data.Entity;
using System.Data.Entity.Core.Objects;
using System.Linq;

public class MeasurementContractsDbContext : BaseDbContext
{
    #region <Constructors>

    [DefaultConstructor]
    public MeasurementContractsDbContext() : base(Settings.ConnectionString.Database.MeasurementContractsDb)
    {
        Database.SetInitializer<MeasurementContractsDbContext>(null);
        Database.CommandTimeout = int.Parse(ConfigurationManager.AppSettings[Settings.Command.TimeoutInterval]);
        Configuration.ProxyCreationEnabled = false;
    }

    #endregion

    #region <Methods>

    [Function(FunctionType.ComposableScalarValuedFunction, nameof(svfn_GetMeterConnection), Schema = "dbo")]
    [return: Parameter(DbType = "varchar")]
    public string svfn_GetMeterConnection(int circuitConnectionId, string connectionTypeName)
    {
        ObjectParameter circuitConnectionIdParameter = new ObjectParameter("CircuitConnectionId", circuitConnectionId);
        ObjectParameter connectionTypeNameParameter = new ObjectParameter("ConnectionTypeName", connectionTypeName);

        return this.ObjectContext().ExecuteFunction<string>(nameof(this.svfn_GetMeterConnection), circuitConnectionIdParameter, connectionTypeNameParameter).SingleOrDefault();
    }

    [Function(FunctionType.ComposableScalarValuedFunction, nameof(svfn_GetCurrentObjectStateName), Schema = "dbo")]
    [return: Parameter(DbType = "varchar")]
    public string svfn_GetCurrentObjectStateName(int contextId, string contextFullName)
    {
        ObjectParameter contextIdParameter = new ObjectParameter("contextId", contextId);
        ObjectParameter contextFullNameParameter = new ObjectParameter("contextFullName", contextFullName);

        return this.ObjectContext().ExecuteFunction<string>(nameof(this.svfn_GetCurrentObjectStateName), contextIdParameter, contextFullNameParameter).SingleOrDefault();
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        // ADD Functions
        modelBuilder.Conventions.Add(new FunctionConvention<MeasurementContractsDbContext>());

        // ...
    }

    #endregion
}
使用EntityFramework.Functions;
使用结构图;
使用系统配置;
使用System.Data.Entity;
使用System.Data.Entity.Core.Objects;
使用System.Linq;
公共类度量contractsdbcontext:BaseDbContext
{
#区域
[默认构造函数]
public MeasurementContractsDbContext():base(Settings.ConnectionString.Database.MeasurementContractsDb)
{
Database.SetInitializer(null);
Database.CommandTimeout=int.Parse(ConfigurationManager.AppSettings[Settings.Command.TimeoutInterval]);
Configuration.ProxyCreationEnabled=false;
}
#端区
#区域
[函数(FunctionType.ComposableScalarValuedFunction,nameof(svfn_GetMeterConnection),Schema=“dbo”)]
[返回:参数(DbType=“varchar”)]
公共字符串svfn_GetMeterConnection(int-circuitConnectionId,string-connectionTypeName)
{
ObjectParameter CircuitConnectiondParameter=新的ObjectParameter(“CircuitConnectionId”,CircuitConnectionId);
ObjectParameter ConnectionTypeName参数=新的ObjectParameter(“ConnectionTypeName”,ConnectionTypeName);
返回此.ObjectContext().ExecuteFunction(nameof(this.svfn_GetMeterConnection)、CircuitConnectionIndParameter、connectionTypeNameParameter);
}
[函数(FunctionType.ComposableScalarValuedFunction,nameof(svfn_GetCurrentObjectStateName),Schema=“dbo”)]
[返回:参数(DbType=“varchar”)]
公共字符串svfn_GetCurrentObjectStateName(int-contextId,string-contextFullName)
{
ObjectParameter contextIdParameter=新的ObjectParameter(“contextId”,contextId);
ObjectParameter contextFullNameParameter=新的ObjectParameter(“contextFullName”,contextFullName);
返回此.ObjectContext().ExecuteFunction(nameof(this.svfn_GetCurrentObjectStateName)、contextIdParameter、contextFullNameParameter);
}
模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
{
基于模型创建(modelBuilder);
//添加函数
添加(新函数convention());
// ...
}
#端区
}
示例用法:

/// <exception cref="ArgumentNullException">Non-Existent 'Query' value throws this exception</exception>
public IQueryable<MeterDetailDataItem> AuthorizationToFlowMeterDocumentFilter(IQueryable<MeterDetailDataItem> query)
{
    if (query == null)
        throw new ArgumentNullException("Query");

    string contextFullName = typeof(AuthorizationToFlowMeterDocument).FullName;

    // Get the ATF (if it exists)
    var filteredQuery = (from dataitem in query //<-- QUERY
                        join document in UnitOfWork.Document on dataitem.RequestToFlowMeterDocumentId equals document.ParentId

                         // TODO: Figure out if you can move svfn_GetCurrentObjectStateName into an Algorythm class that can be injected
                         // SQL Function
                         let objectStateName = ((MeasurementContractsDbContext)UnitOfWork.DbContext).svfn_GetCurrentObjectStateName(document.Id, contextFullName)

                        select new MeterDetailDataItem()
                        {
                            MeterId = dataitem.MeterId,
                            RequestToFlowMeterDocumentId = dataitem.RequestToFlowMeterDocumentId,
                            RequestToFlowMeterDocumentObjectStateName = dataitem.RequestToFlowMeterDocumentObjectStateName,
                            AuthorizationToFlowMeterDocumentId = document.Id,
                            AuthorizationToFlowMeterDocumentObjectStateName = objectStateName,
                            FirstDeliveryNoticeDocumentId = dataitem.FirstDeliveryNoticeDocumentId,
                            FirstDeliveryNoticeDocumentObjectStateName = dataitem.FirstDeliveryNoticeDocumentObjectStateName,
                            FavoriteId = dataitem.FavoriteId
                        });

    return filteredQuery.OrderBy(x => x.MeterId);
}
///不存在的“Query”值引发此异常
公共IQueryable AuthorizationToFlowMeterDocumentFilter(IQueryable查询)
{
if(查询==null)
抛出新的ArgumentNullException(“查询”);
string contextFullName=typeof(AuthorizationToFlowMeterDocument).FullName;
//获取ATF(如果存在)
var filteredQuery=(来自query//x.MeterId中的dataitem);
}
看起来您正在使用软件包。然后看一下主题。您使用的是标量值函数,不可组合,根据软件包作者的说法

可以像上面的其他方法一样直接调用

但是

但是,由于它被指定为不可组合的,所以不能由LINQ中的实体框架转换为实体查询

这是通过实体框架的设计实现的

而您需要的是标量值函数,可组合的

在LINQ to Entities查询中工作,但不能直接调用

简而言之,由于您正在LINQ to Entities查询中使用它,所以请在
函数
注释中使用
FunctionType.ComposableScalarValuedFunction
。由于它不可直接调用,所以不需要方法体,所以您可以简单地抛出异常:

[Function(FunctionType.ComposableScalarValuedFunction, nameof(svfn_GetMeterConnection), Schema = "dbo")]
public string svfn_GetMeterConnection(int circuitConnectionId, string connectionTypeName)
{
    throw new NotSupportedException();
}
不要忘记注册链接中显示的函数,否则它们将无效,您将继续获得
NotSupportedException

modelBuilder.Conventions.Add(new FunctionConvention<TheClassContainingTheFunction>());
modelBuilder.Conventions.Add(新函数convention());

你能为类型\实体发布最小的代码吗?试着用
let
关键字编写它,然后在
选择new
中使用它,所以
…让cV=GetConnection(circuitConnection.Id,connection.ConnectionTypeName)选择new…ConnectionValue=cV…
@RandRandom-不工作只是为了确保,您是否按此处所述调用了
AddFunction
?在进一步阅读之前的链接页面后,我相信我发现了您的问题-您已声明了您的方法
nonposableScalarValuedFunction
,根据此页面,该方法具有不能在LINQ to Entities查询中使用的限制
您需要的是一个
可组合的CalarValuedFunction
查看它们是如何完成的Ivan,谢谢您的回答。我已经试过了……但不幸的是,更改为“可组合”也不起作用。不过,我也会更新我的问题来说明这一点。您是否按照同一链接的“将函数添加到实体模型”部分中的说明注册自定义函数?e、 g.拥有
modelBuilder.Conventions.Add(newfunctionconvention())?现在获取“LINQ to Entities无法识别方法'System.String svfn_GetMeterConnection(Int32,System.String)'方法,一个
[Function(FunctionType.ComposableScalarValuedFunction, nameof(svfn_GetMeterConnection), Schema = "dbo")]
public string svfn_GetMeterConnection(int circuitConnectionId, string connectionTypeName)
{
    throw new NotSupportedException();
}
modelBuilder.Conventions.Add(new FunctionConvention<TheClassContainingTheFunction>());