实体框架6:抑制除零除外

时间:2013-10-07 22:04:12

标签: c# entity-framework asp.net-web-api

我想用实体框架做这样的工作:

db.Customers.OrderBy(c => c.MoneySpent/c.OrdersPlaced)

不必像以下那样对零保护进行任何划分:

db.Customers.OrderBy(c => c.OrdersPlaced == 0 ? 0.0 : c.MoneySpent/c.OrdersPlaced)

为了做到这一点,我尝试了几件事,但我最接近解决问题的方法是禁用ARITHABORTANSI_WARNINGS,这是我在上下文的构造函数中所做的:< / p>

public class DatabaseContext : DbContext
{
    static DatabaseContext()
    {
        Database.SetInitializer<DatabaseContext>(null);
    }

    public DatabaseContext()
        : base("Name=DatabaseContext")
    {
        Database.ExecuteSqlCommand("SET ANSI_DEFAULTS OFF");
        Database.ExecuteSqlCommand("SET ARITHABORT OFF");
        Database.ExecuteSqlCommand("SET ANSI_WARNINGS OFF");
    }

    // Tables that can be queried directly
    public DbSet<Customer> Customers { get; set; }
}

当我尝试从我的Web API控制器获取结果时,我仍然收到以下错误:

...failed to serialize the response body for content type 'application/json...
InnerException: Divide by zero error encountered...

当我看看SQL Server Profiler发生了什么时,似乎ANSI_WARNINGS每当发出新查询时都会重置为ON

SQL Server Profiler

有没有办法可以更改ANSI_WARNINGS的默认值,使其在查询之间保持OFF

3 个答案:

答案 0 :(得分:2)

我认为您的问题是ARITHABORTANSI_WARNINGS命令的范围仅限于交易。

  

默认情况下,从EF6 Database.ExecuteSqlCommand()开始将换行   事务中的命令(如果尚未存在)

您必须设置数据库默认值 - (并接受可能具有的所有含义!)

  

您可以使用sp_configure的用户选项选项来设置默认值   为服务器的所有连接设置ANSI_WARNINGS。更多   信息,see sp_configure (Transact-SQL)

或者您必须使用EF6的improved transaction support

管理您自己的连接/交易

无论哪种方式,要避免编码除以零似乎要做很多工作....

参考文献:

Working with Transactions (EF6 Onwards)

SET ANSI_WARNINGS (Transact-SQL)

答案 1 :(得分:1)

凭借科林的出色答案,我能够实现以下控制器,现在它完美无缺!谢谢!

public class CustomersController : ApiController
{
    private readonly CutomerContext _context = new CustomerContext();
    private DbContextTransaction _transactionContext;

    public IEnumerable<Customer> Get()
    {
        _transactionContext = _context.Database.BeginTransaction();

        _context.Database.ExecuteSqlCommand("SET ANSI_WARNINGS OFF");
        _context.Database.ExecuteSqlCommand("SET ARITHABORT OFF");

        var orderedCustomers = _context.Customers.OrderByDescending(c => ((double)c.MoneySpent)/c.OrdersPlaced);

        return orderedCustomers.AsEnumerable();
    }

    protected override void Dispose(bool disposing)
    {
        _context.Dispose();
        if(_transactionContext != null)
            _transactionContext.Dispose();
    }
}

答案 2 :(得分:0)

您在每个查询中看到重置设置的原因可能与EF默认打开并关闭每个查询的连接这一事实有关。您可以通过手动打开连接来覆盖该行为。

无论如何,我同意这听起来不像是一种强有力的方法。

更好的处理方法可能是确保OrdersPlaced为NULL而不是零(x / NULL不会导致错误但会评估为NULL)。