【问题标题】:Execute SP using Entity Framework and .net Core使用 Entity Framework 和 .net Core 执行 SP
【发布时间】:2018-10-04 15:51:15
【问题描述】:

我使用 .net 核心和实体框架创建了一个 Web api 项目。 这使用了一个存储过程,它返回由实体框架定义的数据库表的大部分属性。

实体框架不会带回表的所有列。当我调用 api 时出现错误,抱怨当我使用 执行存储过程时找不到丢失的列,

_context.Set<TableFromSql>().FromSql("execute dbo.spr_GetValue").ToList();

我创建了另一个模型类,它定义了从 SP 带回的属性(称为 NewClass)。

_context.Set<NewClass>().FromSql("execute dbo.spr_GetValue").ToList();

这可行,但只是想检查是否存在 SP 应仅从数据库返回模型类的约定。

【问题讨论】:

  • docs。它在限制下声明:SQL 查询必须返回实体或查询类型的所有属性的数据
  • 这是否意味着我不应该添加自己的模型类并更改 SP 以返回实体的所有属性。那么真正复杂的 sql SP 不需要返回整个列(可能是在数据库中的表之间使用连接)呢?
  • 不,这不是我的意思。 EF Core(自 2.1 起)有两种类型的模型:实体(映射到数据库中的表)和“查询类型”(如 Tau Zhou 在他的回答中提到的)。查询类型不映射到数据库表。从您的查询来看,TableFromSql 是否是模型并不是很清楚。但无论如何,上述限制适用于:实体和查询类型。如果TableFromSql 是一个实体并且该过程仅返回属性的子集,则应改用查询类型

标签: entity-framework asp.net-core asp.net-core-webapi


【解决方案1】:

SQL 查询必须返回实体或查询类型的所有属性的数据

这个限制是在将sql查询结果映射到Model时造成的。它遍历模型中的属性并尝试从查询结果中检索值。如果模型属性在query result 中不存在,则会抛出错误。

如果您想返回所需的列而不是所有列,一种选择是通过Query 定义返回的模型。

对于您的演示代码,您可以在OnModelCreating 中定义它。

builder.Query<TableFromSql>();

注意,为此,您需要确保TableFromSql 中的所有属性都存在于execute dbo.spr_GetValue 中。

另一种方式,您可以实现自己的FromSql,它将添加条件以检查查询结果中是否存在属性。

    public static class DbContextExtensions
{
    public static List<T> RawSqlQuery<T>(this DbContext context,string query)
    {
        using (var command = context.Database.GetDbConnection().CreateCommand())
        {
            command.CommandText = query;
            command.CommandType = CommandType.Text;

            context.Database.OpenConnection();

            using (var result = command.ExecuteReader())
            {
                var entities = new List<T>();

                return DataReaderMapToList<T>(result);
            }
        }
    }
    public static List<T> DataReaderMapToList<T>(IDataReader dr)
    {
        List<T> list = new List<T>();
        T obj = default(T);
        while (dr.Read())
        {
            obj = Activator.CreateInstance<T>();
            foreach (PropertyInfo prop in obj.GetType().GetProperties())
            {
                if (ColumnExists(dr, prop.Name))
                {
                    if (!object.Equals(dr[prop.Name], DBNull.Value))
                    {

                        prop.SetValue(obj, dr[prop.Name], null);
                    }
                }
            }
            list.Add(obj);
        }
        return list;
    }
    public static bool ColumnExists(IDataReader reader, string columnName)
    {

        return reader.GetSchemaTable()
                     .Rows
                     .OfType<DataRow>()
                     .Any(row => row["ColumnName"].ToString() == columnName);
    }
}

使用上面的代码:

        var result = _context.RawSqlQuery<ToDoItemVM>("execute [dbo].[get_TodoItem]");

【讨论】:

  • 如上所述,我创建了一个名为“NewClass.cs”的新模型类,并将其添加到 context.cs 中,例如 modelBuilder.Entity(entity => { entity.HasKey(e => e.LocationId); });这很好用。但我只是想知道这是否是错误的做法?
  • @liroforum 这是正确且当前推荐的从 sp 返回自定义列的方法。 builder.Query 用于返回自定义模型。
  • 所以我已将新类添加到模型构建器中,如 modelBuilder.Query();并在控制器中使用它,例如 _context.Query().FromSql("execute dbo.spr_GetValue").ToList(); .这是正确的还是你的意思是别的?是否可以像docs.microsoft.com/en-us/ef/core/modeling/query-types 中那样从 context.cs 调用 sql SP?
  • 感谢涛,帮了大忙。
猜你喜欢
  • 1970-01-01
  • 2018-10-28
  • 1970-01-01
  • 1970-01-01
  • 2020-02-25
  • 2017-04-20
  • 1970-01-01
  • 1970-01-01
  • 2017-01-19
相关资源
最近更新 更多