ef-core中带有嵌套查询的SQL数据投影

时间:2019-08-16 06:08:58

标签: c# linq entity-framework-core

有一个消息表。方案:

CREATE TABLE [dbo].[Messages] (
    [Id]          INT            IDENTITY (1, 1) NOT NULL,
    [DateCreate]  DATETIME2 (7)  DEFAULT (getdate()) NOT NULL,
    [SenderId]    INT            NOT NULL,
    [RecipientId] INT            NOT NULL,
    [TextMessage] NVARCHAR (MAX) NULL,
    [IsReaded]    BIT            NOT NULL,
    CONSTRAINT [PK_Messages] PRIMARY KEY CLUSTERED ([Id] ASC)
);

我想要一张桌子:

  • sender_id [int]-发件人ID键
  • all_count_messages [int]-发件人发来的邮件数量
  • count_unreaded_messages [int]-发件人未读消息的数量
  • most_unreaded [DateTime]-最旧的未读邮件的日期

我还需要按[sender_id]分组并对输出进行排序。好。我提出一个简单的要求:

SELECT
    message.SenderId AS sender_id,
    COUNT(*) AS all_count_messages,
    SUM(CAST(
        CASE
            WHEN IsReaded = 0
                     THEN 1
                  ELSE 0
             END AS int)) as count_unreaded_messages,
    MIN(message.DateCreate) AS most_unreaded
FROM
    Messages AS message
GROUP BY
    message.SenderId
ORDER BY
    most_unreaded

演示结果:

sender_id   all_count_messages  unreaded_messages   most_unreaded
2   3   2   2019-08-15 20:03:59.0000000
1   9   8   2019-08-15 20:04:59.0000000

答案很适合我。如何在EFCore上描述它?

尝试

var chats = from my_messages in db.Messages
                             group my_messages by my_messages.SenderId into g
                             select
                             new
                             {
                                 sender_id = g.Key,
                                 all_count_messages = g.Count(),
                                 unreaded_messages = from sub_messages in db.Messages where sub_messages.SenderId == g.Key && !sub_messages.IsReaded group sub_messages by sub_messages.SenderId into sub_g select sub_g.Count(),
                                 most_unreaded = from sub_messages in db.Messages where sub_messages.SenderId == g.Key && !sub_messages.IsReaded group sub_messages by sub_messages.SenderId into sub_g select sub_g.Min(x => x.DateCreate)
                             };


                foreach (var chat in chats) // so, too, has tried: chats.Include(x=>x.unreaded_messages).Include(x => x.most_unreaded)
                {

                }

foreach (var chat in chats)中出现错误

An unhandled exception occurred while processing the request.
ArgumentException: must be reducible node
System.Linq.Expressions.Expression.ReduceAndCheck()

我尝试其他方式:

 var chats = db.Messages.AsNoTracking().FromSql(
                "SELECT" +
                "    message.SenderId AS sender_id," +
                "    COUNT(*) AS all_count_messages," +
                "    SUM(CAST(" +
                "      CASE" +
                "        WHEN IsReaded = 0" +
                "        THEN 1" +
                "        ELSE 0" +
                "      END AS int)) as count_unreaded_messages," +
                "    MIN(message.DateCreate) AS most_unreaded " +
                "FROM " +
                "    Messages AS message " +
                "GROUP BY " +
                "    message.SenderId " +
                "ORDER BY" +
                "    most_unreaded ");
foreach (var chat in chats)
                {

                }

foreach (var chat in chats)中出现错误

InvalidOperationException: The required column 'Id' was not present in the results of a 'FromSql' operation.

Microsoft.EntityFrameworkCore.Query.Sql.Internal.FromSqlNonComposedQuerySqlGenerator.CreateValueBufferFactory(IRelationalValueBufferFactoryFactory relationalValueBufferFactoryFactory, DbDataReader dataReader)

2 个答案:

答案 0 :(得分:0)

您需要在查询的select语句中添加Id列。应该是

 "SELECT" +
                "    message.Id AS Id," +
                "    message.SenderId AS sender_id," +
...

答案 1 :(得分:0)

感谢@ilkerkaran那里的链接https://stackoverflow.com/a/28627991/2630427,我发现并稍微改变了动态请求的方法

.net asp core 2.2的一点完成的实现:

public class AppDbContext : DbContext
    {

        public DbSet<Message> Messages { get; set; }

        IOptions<AppConfig> app_config;

        public AppDbContext(DbContextOptions<AppDbContext> options, IOptions<AppConfig> _app_config)
            : base(options)
        {
            app_config = _app_config;
            Database.EnsureCreated();
        }


        public IEnumerable<dynamic> DynamicListFromSql(string Sql, Dictionary<string, object> Params = null)
        {
            using (var cmd = Database.GetDbConnection().CreateCommand())
            {
                cmd.CommandText = Sql;
                if (cmd.Connection.State != ConnectionState.Open) { cmd.Connection.Open(); }

                if (Params != null)
                    foreach (KeyValuePair<string, object> p in Params)
                    {
                        DbParameter dbParameter = cmd.CreateParameter();
                        dbParameter.ParameterName = p.Key;
                        dbParameter.Value = p.Value;
                        cmd.Parameters.Add(dbParameter);
                    }

                using (var dataReader = cmd.ExecuteReader())
                {
                    while (dataReader.Read())
                    {
                        var row = new ExpandoObject() as IDictionary<string, object>;
                        for (var fieldCount = 0; fieldCount < dataReader.FieldCount; fieldCount++)
                        {
                            row.Add(dataReader.GetName(fieldCount), dataReader[fieldCount]);
                        }
                        yield return row;
                    }
                }
            }
        }

    }
string query =
                "SELECT" +
                "    message.SenderId AS sender_id," +
                "    COUNT(*) AS all_count_messages," +
                "    (SELECT COUNT(*) FROM Messages AS sub_message WHERE sub_message.SenderId = message.SenderId AND sub_message.IsReaded = 0) AS unreaded_messages," +
                "    (SELECT MIN(sub_message.DateCreate) FROM Messages AS sub_message WHERE sub_message.SenderId = message.SenderId) AS most_unreaded " +
                "FROM " +
                "    Messages AS message " +
                "GROUP BY " +
                "    message.SenderId " +
                "ORDER BY" +
                "    most_unreaded ";
                //"OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY", offset, fetch);

                foreach (var chat in db.DynamicListFromSql(query))
                {

                }