9

在 MVC 4 和 EF 5 中,我想运行动态查询。

var returndata = Context.Database.SqlQuery(Type, strsql, null);

我不知道,它将返回和命名多少个字段。根据这个结果,我想制作将在视图中显示的表格结构。

问题:我应该作为类型传递什么?

我的查询返回以下结果:

第 1 场、第 2 场、第 3 场、第 4 场、第 5 场

第 1 行...

第2行..

感谢任何建议。

4

8 回答 8

25

您可以使用原始 SQL 查询,因为 EF 不支持:

private static IEnumerable<object[]> Read(DbDataReader reader)
{
    while (reader.Read())
    {
        var values = new List<object>();
        for (int i = 0; i < reader.FieldCount; i++)
        {
            values.Add(reader.GetValue(i));
        }
        yield return values.ToArray();
    }
}

进而:

public ActionResult Index()
{
    using (var ctx = new UsersContext())
    using (var cmd = ctx.Database.Connection.CreateCommand())
    {
        ctx.Database.Connection.Open();
        cmd.CommandText = "SELECT * FROM UserProfile";
        using (var reader = cmd.ExecuteReader())
        {
            var model = Read(reader).ToList();
            return View(model);
        }
    }
}

最后在你看来:

@model IEnumerable<object[]>
<table>
    <tbody>
        @foreach (var row in Model)
        {
            <tr>
                @foreach (var column in row)
                {
                    <td>@column</td>
                }
            </tr>
        }
    </tbody>
</table>
于 2013-03-31T17:51:57.393 回答
11

此方法将数据从 SQL select(带参数)加载到行列表中,其中每一行是列的字典(键是列名)。

private static List<Dictionary<string, object>> LoadData(string sqlSelect, params object[] sqlParameters)
{
    var table = new List<Dictionary<string, object>>();
    using (var ctx = new DbEntities())
    {
        ctx.Database.Connection.Open();
        using (var cmd = ctx.Database.Connection.CreateCommand())
        {
            cmd.CommandText = sqlSelect;
            foreach (var param in sqlParameters)
                cmd.Parameters.Add(param);
            using (var reader = cmd.ExecuteReader())
            {
                while (reader.Read())
                {
                    var row = new Dictionary<string, object>();
                    for (int i = 0; i < reader.FieldCount; i++)
                        row[reader.GetName(i)] = reader[i];
                    table.Add(row);
                }
            }
        }
    }
    return table;
}
于 2015-02-09T09:02:17.107 回答
6

最后,我使用“Mortalus”和 ExpandoObject 对象建议的 TypeBuilder 选项。它现在几乎没有性能开销。

从“Mortalus”答案中获取 Typebuilder 代码,然后我根据我的要求编写代码,如下所示。

List<Dictionary<string, object>> expandolist = new List<Dictionary<string, object>>();

foreach (var item in returndata)
  {
  IDictionary<string, object> expando = new ExpandoObject();
  foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(item))
     {
      var obj = propertyDescriptor.GetValue(item);
      expando.Add(propertyDescriptor.Name, obj);
     }
     expandolist.Add(new Dictionary<string, object>(expando));
  }

  return expandolist;

所以现在,我有来自动态对象的“字典”对象。并使用它,您可以在设计时轻松工作,而不是等到运行时使用“动态”对象。

于 2013-04-05T14:29:39.300 回答
0

在不知道返回的类型的情况下,我认为您可能不走运。

如果您知道它可能属于哪些模式,则可以在try { } catch () { }与您的其他动态查询中的这些参数匹配的接口上使用一些 's,但这似乎有点痛苦。

于 2013-03-31T18:08:25.963 回答
0

不幸的是,除非 EF 知道对象的Type.

如果这对您真的有必要,我认为您最好的选择是退回到ADO.NETand DataTable

于 2013-03-31T18:16:39.163 回答
0

我最近偶然发现了这个例子:

http://www.markzhou.com/blog/post/2011/06/02/Use-dynamic-type-in​​-Entity-Framework-41-SqlQuery()-method.aspx

我没有时间自己测试它,但似乎可以通过一些额外的工作来构造动态类型。

简而言之,你会想做这样的事情:

  TypeBuilder builder = Program.CreateTypeBuilder(
                "MyDynamicAssembly", "MyModule", "MyType");
  Program.CreateAutoImplementedProperty(builder, "name", typeof(string));
  Program.CreateAutoImplementedProperty(builder, "type", typeof(string));
  Program.CreateAutoImplementedProperty(builder, "id", typeof(int));

  Type resultType = builder.CreateType();
  dynamic queryResult = context.Database.SqlQuery(
                    resultType, "SELECT * FROM sys.sysobjects");

TypeBuilder在我附上的帖子中详细描述了哪里。

于 2013-03-31T18:21:29.803 回答
0

Darin Dimitrov 的类似帖子,但它返回 DataTable

public DataTable QueryToTable(Entities db, string queryText, SqlParameter[] parametes)
        {
            using ( DbDataAdapter adapter = new SqlDataAdapter())
            {
                adapter.SelectCommand = db.Database.Connection.CreateCommand();
                adapter.SelectCommand.CommandText = queryText;
                if (parametes != null)
                    adapter.SelectCommand.Parameters.AddRange(parametes);
                DataTable table = new DataTable();
                adapter.Fill(table);
                return table;
            }
        }

采用

SqlParameter[] parametes = new[]
                {
                    new SqlParameter("date_from", dateFrom)
                };

DataTable tab = QueryToTable(new Entities(), 
               "Select *  From SomeTable Where ADate >= @date_from", parametes);

MS SQL Server 示例

于 2013-12-24T06:20:58.130 回答
0

添加到 Petr Voborník 的答案,动态查询,我添加了 ResultSet 的动态插入,我的应用程序对整个数据库的所有表进行动态查询,一次一个块,然后将动态结果插入远程数据库,使用 Always Encrypted (此处省略)。传递一个 sb 命令和参数对象。

    public void StoreData(DbContext dbContext, Dictionary<string, string> columnInfo, List<Dictionary<string, object>> multiInsertObj, string tableName)
    {
        _ctx = dbContext;
        _columnInfo = columnInfo;
        var sb = new StringBuilder();
        sb.Append(BuildSqlCommand(tableName, columnInfo, multiInsertObj.Count));
        ExecuteSqlCommand(sb, GetParamsObject(columnInfo, multiInsertObj));
    }

    private static StringBuilder BuildSqlCommand(string tableName, Dictionary<string, string> variableInfo, int variableCount)
    {
        //Build sql command
        var sb = new StringBuilder();
        sb.Append("INSERT INTO dbo." + tableName + "(");
        foreach (var variable in variableInfo)
        {
            sb.Append(variable.Key);
            sb.Append(", ");
        }
        sb.Append("SystemNumber, ");
        sb.Remove(sb.Length - 2, 2).Append(") VALUES ");
        for (var i = 0; i < variableCount; i++)
        {
            sb.Append("(");
            foreach (var name in variableInfo.Keys)
            {
                sb.Append("@" + name + "_" + i + ",");
            }
            sb.Append("@SystemNumber" + "_" + i + ",");
            sb.Remove(sb.Length - 1, 1).Append("),");
        }
        sb.Remove(sb.Length - 1, 1);
        return sb;
    }

    private static object[] GetParamsObject(Dictionary<string, string> columnInfo, List<Dictionary<string, object>> multiInsertObj)
    {
        var variableCount = multiInsertObj.Count;
        var rowCount = multiInsertObj[0].Keys.Count;
        var objectLength = (rowCount + 1) * variableCount;
        var variableDataTypes = columnInfo.Values.ToList();
        var paramObj = new object[objectLength];
        var j = 0;
        var i = 0;
        foreach (var row in multiInsertObj)
        {
            var k = 0;
            foreach (var data in row)
            {
                var sb = new StringBuilder();
                sb.Append("@");
                sb.Append(data.Key);
                sb.Append("_" + i);
                paramObj[j] = new SqlParameter(sb.ToString(), SetSqlDataType(variableDataTypes[k])) { Direction = Input, Value = data.Value };
                j++;
                k++;
            }
            paramObj[j] = new SqlParameter(("@SystemNumber" + "_" + i), SetSqlDataType("int")) { Direction = Input, Value = _systemNumber };
            i++;
            j++;
        }
        return paramObj;
    }

    private static void ExecuteSqlCommand(StringBuilder sb, params object[] sqlParameters)
    {
        using (_ctx)
        {
            _ctx.Database.Connection.Open();
            using (var cmd = _ctx.Database.Connection.CreateCommand())
            {
                cmd.CommandText = sb.ToString();
                foreach (var param in sqlParameters)
                    cmd.Parameters.Add(param);
                try
                {
                    cmd.ExecuteNonQuery();
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    throw;
                }
            }
        }
    }
于 2017-06-01T14:28:38.073 回答