使用 C# 的 SQL UPDATE 语句的 Lambda 表达式

发布于 2024-10-18 15:08:02 字数 516 浏览 1 评论 0原文

是否可以使用库或代码从 lambda 表达式创建 SQL Update 语句?我们希望使用强类型 lambda 表达式来进行更新,而不是事先调用对象或使用字符串。我正在考虑这样的事情。

Update<Task>(
    u => u.UserID = 1, u.TaskCount += 1, //Update
    w => w.Priority != "High" && (w.Status != "Complete" || w.Status == null) //Where
);

这大致可以翻译为..

UPDATE Tasks SET UserID = 1, TaskCount = TaskCount + 1
WHERE Priority <> "High" AND (Status <> "Complete" OR Status = null)

我应该提到我们目前正在使用实体框架和 Postgres。

Is library or code available to create SQL Update statements from lambda expressions? We would like to use strongly-typed lambda expressions to do updates instead of calling the object before hand, or using strings. I'm thinking of something like this.

Update<Task>(
    u => u.UserID = 1, u.TaskCount += 1, //Update
    w => w.Priority != "High" && (w.Status != "Complete" || w.Status == null) //Where
);

Which would roughly translate to..

UPDATE Tasks SET UserID = 1, TaskCount = TaskCount + 1
WHERE Priority <> "High" AND (Status <> "Complete" OR Status = null)

I should mention we are currently using the Entity Framework and Postgres.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

江心雾 2024-10-25 15:08:02

我终于找到了一种方法来做到这一点。基本上,从实体框架、LINQ-to-SQL 或其他 ORM 获取生成的 SQL,然后解析 WHERE 子句。这样我就不必手动解析 lambda 了。然后从匿名类型创建 UPDATE 子句。结果如下:

Update<Task>(
    new { UserID = 1, TaskCount = IncrementOf(1), SomeOtherField = DdNull } //Update
    w => w.Priority != "High" && (w.Status != "Complete" || w.Status == null) //Where
);

Delete<Task>(w => w.UserID == userID && w.Status != "Complete");

这允许我更新/删除值,而无需先拉取它们。

它的代码看起来像这样......

protected void Update<T>(object values, Expression<Func<T, bool>> where) where T : class
{
    Domain.ExecuteStoreCommand(
        "UPDATE {0} SET {1} WHERE {2};",
        GetTableString<T>(),
        GetUpdateClauseString(values),
        GetWhereClauseString(where)
        );
}

protected string GetUpdateClauseString(object obj)
{
    string update = "";
    var items = obj.ToDictionary();
    foreach (var item in items)
    {
        //Null
        if (item.Value is DBNull) update += string.Format("{0} = NULL", GetFieldString(item.Key));

        //Increment
        else if (item.Value is IncrementExpression) update += string.Format("{0} = {0} + {1}", GetFieldString(item.Key), ((IncrementExpression)item.Value).Value.ToString());

        //Decrement
        else if (item.Value is DecrementExpression) update += string.Format("{0} = {0} - {1}", GetFieldString(item.Key), ((DecrementExpression)item.Value).Value.ToString());

        //Set value
        else update += string.Format("{0} = {1}", GetFieldString(item.Key), GetValueString(item.Value));

        if (item.Key != items.Last().Key) update += ", ";
    }
    return update;
}

protected string GetWhereClauseString<T>(Expression<Func<T, bool>> where) where T : class
{
    //Get query
    var query = ((IQueryable<T>)Domain.CreateObjectSet<T>());
    query = query.Where(where);
    ObjectQuery queryObj = (ObjectQuery)query;

    //Parse where clause
    string queryStr = queryObj.ToTraceString();
    string whereStr = queryStr.Remove(0, queryStr.IndexOf("WHERE") + 5);

    //Replace params
    foreach (ObjectParameter param in queryObj.Parameters)
    {
        whereStr = whereStr.Replace(":" + param.Name, GetValueString(param.Value));
    }

    //Replace schema name
    return whereStr.Replace("\"Extent1\"", "\"Primary\"");
}

I finally figured out a way to do this. Basically, get the generated SQL from the Entity Framework, LINQ-to-SQL, or another ORM, then parse the WHERE clause. That way I don't have to parse the lambda manually. Then create an UPDATE clause from an anonymous type. The result looks like:

Update<Task>(
    new { UserID = 1, TaskCount = IncrementOf(1), SomeOtherField = DdNull } //Update
    w => w.Priority != "High" && (w.Status != "Complete" || w.Status == null) //Where
);

Delete<Task>(w => w.UserID == userID && w.Status != "Complete");

This allows me to update/delete values WITHOUT pulling them first.

And the code for it looks like this...

protected void Update<T>(object values, Expression<Func<T, bool>> where) where T : class
{
    Domain.ExecuteStoreCommand(
        "UPDATE {0} SET {1} WHERE {2};",
        GetTableString<T>(),
        GetUpdateClauseString(values),
        GetWhereClauseString(where)
        );
}

protected string GetUpdateClauseString(object obj)
{
    string update = "";
    var items = obj.ToDictionary();
    foreach (var item in items)
    {
        //Null
        if (item.Value is DBNull) update += string.Format("{0} = NULL", GetFieldString(item.Key));

        //Increment
        else if (item.Value is IncrementExpression) update += string.Format("{0} = {0} + {1}", GetFieldString(item.Key), ((IncrementExpression)item.Value).Value.ToString());

        //Decrement
        else if (item.Value is DecrementExpression) update += string.Format("{0} = {0} - {1}", GetFieldString(item.Key), ((DecrementExpression)item.Value).Value.ToString());

        //Set value
        else update += string.Format("{0} = {1}", GetFieldString(item.Key), GetValueString(item.Value));

        if (item.Key != items.Last().Key) update += ", ";
    }
    return update;
}

protected string GetWhereClauseString<T>(Expression<Func<T, bool>> where) where T : class
{
    //Get query
    var query = ((IQueryable<T>)Domain.CreateObjectSet<T>());
    query = query.Where(where);
    ObjectQuery queryObj = (ObjectQuery)query;

    //Parse where clause
    string queryStr = queryObj.ToTraceString();
    string whereStr = queryStr.Remove(0, queryStr.IndexOf("WHERE") + 5);

    //Replace params
    foreach (ObjectParameter param in queryObj.Parameters)
    {
        whereStr = whereStr.Replace(":" + param.Name, GetValueString(param.Value));
    }

    //Replace schema name
    return whereStr.Replace("\"Extent1\"", "\"Primary\"");
}
梦里人 2024-10-25 15:08:02

您可以执行类似的操作,但是可以将哪些内容转换为 SQL 以及需要将哪些内容拉回到您的应用程序中。

您需要做的是为您的 Update 方法提供一个 Action (这是“更新”部分)和一个 Expression (作为“ where' 子句)。

public void Update(Action<T> updateStatement, Expression<Func<T, bool>> where)
{
    // get your object context & objectset, cast to IQueryable<T>
    var table = (IQueryable<T>)objectContext.CreateObjectSet<T>();        

    // filter with the Expression
    var items = table.Where(where);

    // perform the Action on each item
    foreach (var item in items)
    {
        updateStatement(item);
    }

    // save changes.
}

然后你可以用类似的方式调用你的更新

repository.Update(s => s.Name = "Me", w => w.Id == 4);

You can do something like this, but there will be limitations on what can be translated into SQL and what needs to be pulled back to your application.

What you need to do is give your Update method both an Action (this is the 'update' part) and an Expression (as the 'where' clause).

public void Update(Action<T> updateStatement, Expression<Func<T, bool>> where)
{
    // get your object context & objectset, cast to IQueryable<T>
    var table = (IQueryable<T>)objectContext.CreateObjectSet<T>();        

    // filter with the Expression
    var items = table.Where(where);

    // perform the Action on each item
    foreach (var item in items)
    {
        updateStatement(item);
    }

    // save changes.
}

Then you can call your Update with something like

repository.Update(s => s.Name = "Me", w => w.Id == 4);
秋意浓 2024-10-25 15:08:02

对于那些喜欢扩展的人:

public static async Task Update<T>(this DbSet<T> objectContext, Action<T> updateStatement, Expression<Func<T, bool>> where) where T : class
    {
        var items = objectContext.AsQueryable();

        // filter with the Expression if exist
        if (where != null)
            items = items.Where(where);

        // perform the Action on each item
        await items.ForEachAsync(updateStatement);
    }

用法:

await context.Organisations.Update(s => s.LastDateBasicEvent = LastDayOfSchool, null);
context.SaveChanges();

在 EF6 上测试

And for those who like Extensions:

public static async Task Update<T>(this DbSet<T> objectContext, Action<T> updateStatement, Expression<Func<T, bool>> where) where T : class
    {
        var items = objectContext.AsQueryable();

        // filter with the Expression if exist
        if (where != null)
            items = items.Where(where);

        // perform the Action on each item
        await items.ForEachAsync(updateStatement);
    }

Usage:

await context.Organisations.Update(s => s.LastDateBasicEvent = LastDayOfSchool, null);
context.SaveChanges();

Tested on EF6

南薇 2024-10-25 15:08:02

我发现这篇关于构建和执行“实体框架之上的 SQL 更新”的文章。也许它对你有用。

http://blogs.msdn.com/b/alexj/archive/2007/12/07/rolling-your-own-sql-update-on-top-of-the -entity-framework-part-1.aspx

I found this article about building and executing an "SQL update on-top of the Entity Framework". Maybe it's useful for you.

http://blogs.msdn.com/b/alexj/archive/2007/12/07/rolling-your-own-sql-update-on-top-of-the-entity-framework-part-1.aspx

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文