2

我想用实体框架做这样的事情:

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

无需像这样设置任何除以零的保护:

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

为了做到这一点,我尝试了几件事,但我得到的最接近解决问题的是禁用ARITHABORTand ANSI_WARNINGS,我在上下文的构造函数中这样做了:

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 探查器

有没有办法改变默认值ANSI_WARNINGS,使其停留OFF在查询之间?

4

3 回答 3

2

我认为您的问题是ARITHABORTandANSI_WARNINGS命令的范围仅限于事务。

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

您要么必须设置数据库默认值 - (并接受所有可能产生的影响!)

您可以使用 sp_configure 的用户选项选项来设置与服务器的所有连接的 ANSI_WARNINGS 的默认设置。有关详细信息,请参阅 sp_configure (Transact-SQL)

或者您将不得不使用 EF6改进的事务支持来管理您自己的连接/事务

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

参考:

处理事务(EF6 以上)

设置 ANSI_WARNINGS (Transact-SQL)

于 2013-10-08T08:28:04.650 回答
1

借助 Colin 的出色回答,我能够实现以下控制器,现在它可以正常工作了!谢谢!

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();
    }
}
于 2013-10-08T19:12:12.500 回答
0

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

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

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

于 2013-10-08T07:09:30.767 回答