.NET Core Entity Framework 存储过程

7

我试图将一个ASP.NET 4.5应用程序移植到.NET Core,但是有一个真正的问题我似乎无法解决。

我的现有应用程序执行返回多个数据表的数据集的存储过程。Entity Framework可以自动将返回的字段映射到我的实体属性,但仅适用于数据集中的第一个数据表(自然而然)。

因此,我正在尝试弄清楚是否有可能以某种方式拦截模型构建过程,并使用自定义代码处理数据集并查看其他数据表以设置实体字段。

我知道我可以使用直接使用SqlConnection执行存储过程的常规方法,但我想知道Entity Framework是否已经有了这样做的方法。


也许这个答案有帮助?https://dev59.com/02kw5IYBdhLWcg3wZJl9#9987939 - Craig Curtis
我刚刚注意到,在.NET Core中,DataTable、DataSet及其所有相关类都不再受支持,因为它们被认为是遗留的。所以我必须使用DbReader代替。我的假设正确吗? - DKhanaf
我认为你可以这样执行存储过程:dbContext.TableName.FromSql("stored_proc")。不过,我不确定这是否会将所有其他表中的字段都映射出来。 - Craig Curtis
4个回答

13

目前,执行返回数据的存储过程的方式是使用DbSet.FromSql方法。

using (var context = new SampleContext())
{
    var data= context.MyEntity
        .FromSql("EXEC GetData")
        .ToList();
}

这种方法有一定的限制:
  • 它必须在 DbSet 上调用
  • 返回的数据必须映射到 DbSet 类型上的所有属性
  • 它不支持临时对象。
或者您可以退回到普通的 ADO.NET:
using (var context = new SampleContext())
using (var command = context.Database.GetDbConnection().CreateCommand())
{
    command.CommandText = "GetData";
    command.CommandType = CommandType.StoredProcedure;
    context.Database.OpenConnection();
    using (var result = command.ExecuteReader())
    {
        // do something with result
    }
}

计划在某个阶段引入支持从SQL查询返回临时类型的功能


这基本上也是我从所有研究中得出的结论。解决我的问题的一种方法是引入一个DAO对象,该对象具有将映射到存储过程返回的所有字段的属性。然后使用可能的隐式转换将DAO转换为我的业务对象,应用所有必要的逻辑来转换字段等... - DKhanaf
仅剩的一个问题是存储过程返回了多个数据集。我能想到的解决方法是将存储过程拆分成两个,然后单独调用它们,然后按照DAO的模式,在服务层将这两个DAO合并到我的业务对象中。现在可能会比以前效率稍低,因为我要进行两次调用,但从某种意义上来说更加清晰和易读。 - DKhanaf
在2018年似乎仍然缺少这个功能,我是对的吗? - Muflix
1
@Muflix 你可以使用查询类型:https://www.learnentityframeworkcore.com/query-types - Mike Brind

1
使用DataAdaptor,以下是使用纯ADO.NET代码调用存储过程的方法:
            DataSet dataSet = null;

            using (var context = new SampleContext())
            using (var command = context.Database.GetDbConnection().CreateCommand())
            {
                command.CommandText = "GetData";
                command.CommandType = CommandType.StoredProcedure;
                context.Database.OpenConnection();
                using (SqlDataAdapter adapter = new SqlDataAdapter())
                {
                    adapter.SelectCommand = command;

                    dataSet = new DataSet();
                    adapter.Fill(dataSet);
                    return dataSet;
                }
            } 

0

为了回答@DKhanaf关于多个数据集的问题,您可以使用SqlDataAdapter将所有结果集填充到DataSet对象中。不过SqlDataAdapter需要完整的.NET Framework,所以您必须在针对.NET 462或类似框架的情况下运行您的.NETCore项目。

         using (var context = new SampleContext())
            using (var command = context.Database.GetDbConnection().CreateCommand())
            {
                command.CommandText = "GetData";
                command.CommandType = CommandType.StoredProcedure;
                context.Database.OpenConnection();
                using (SqlDataAdapter adapter = new SqlDataAdapter(command))
                {
                    var ds = new DataSet();
                    adapter.Fill(ds);
                    return ds;
                }

            }
        }

context.Database.GetDbConnection().CreateCommand() 返回的是 DbCommand 对象,与 SqlDataAdapter 不兼容。 - Nilay Vishwakarma

0

我使用了StoredProcedureEFCore nuget包,链接为https://github.com/verdie-g/StoredProcedureEFCore,EnterpriseLibrary.Data.NetCore,EFCor.SqlServer,EFCore.Tools

我尝试使用{仓储模式}的DbFirst方法.. 我想是这样

startup.cs

ConfigureServices(IServiceCollection services){
    services.AddDbContext<AppDbContext>(opt => opt
                   .UseSqlServer(Configuration.GetConnectionString("SampleConnectionString")));
    services.AddScoped<ISomeDAL, SomeDAL>();

}
            
    public  class AppDbContext : DbContext{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    {}
}

ISomeDAl接口有{GetPropertiesResponse GetAllPropertiesByCity(int CityId);}

public class SomeDAL : ISomeDAL
{
     private readonly AppDbContext context;

     public SomeDAL(AppDbContext context)
         {
             this.context = context;
         }
     public  GetPropertiesResponse GetAllPropertiesByCity(int CityId)
     {
         //Create Required Objects for response 
         //wont support ref Objects through params
         context.LoadStoredProc(SQL_STATEMENT)
            .AddParam("CityID", CityId).Exec( r =>
             {
                  while (r.Read())
                  {

                       ORMapping<GenericRespStatus> orm = new  ORMapping<GenericRespStatus>();
                       orm.AssignObject(r, _Status);
                  }

                  if (r.NextResult())
                  {

                       while (r.Read())
                       {
                           Property = new Property();
                           ORMapping<Property> orm = new ORMapping<Property>();
                           orm.AssignObject(r, Property);
                           _propertyDetailsResult.Add(Property);
                       }
                  }    
           });
    return new GetPropertiesResponse{Status=_Status,PropertyDetails=_propertyDetailsResult}; 
    }
}

public class GetPropertiesResponse
{
     public GenericRespStatus Status;
     public List<Property> PropertyDetails;
     public GetPropertiesResponse()
         {
             PropertyDetails = new List<Property>();
         }
}
public class GenericRespStatus
{
     public int ResCode { get; set; }
     public string ResMsg { get; set; }
}
internal class ORMapping<T>
{
    public void AssignObject(IDataReader record, T myClass)
    {
        PropertyInfo[] propertyInfos = typeof(T).GetProperties();
        for (int i = 0; i < record.FieldCount; i++)
        {
            if (propertyInfos.Any(obj => obj.Name == record.GetName(i))) //&& record.GetValue(i) != DBNull.Value
            {
                propertyInfos.Single(obj => obj.Name == record.GetName(i)).SetValue(myClass, Convert.ChangeType(record.GetValue(i), record.GetFieldType(i)));
            }
        }
    }
}

请阅读编辑帮助,学习如何正确格式化您的答案和问题。此外,请不要在多个问题上粘贴相同的答案。如果两个问题可以用相同的答案回答,您应该回答一个并将另一个标记为重复。 - David Buck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接