优化循环内的LINQ查询

时间:2014-03-18 14:50:57

标签: c# linq

我有一段代码可以确保客户的地址与用户界面中的修改同步:

var customerAddresses = customer.CustomerAddresses.Select(x => x.Address);

// add address to customer if it does not already exist
foreach (var addressModel in model.Addresses)
{
    // make sure an address matches all properties
    Func<Address, bool> addressFilter = x => x.Id == addressModel.Id &&
             x.Street.Equals(addressModel.Street, StringComparison.OrdinalIgnoreCase) &&
             x.City.Equals(addressModel.City, StringComparison.OrdinalIgnoreCase) &&
             x.Province.Equals(addressModel.Province, StringComparison.OrdinalIgnoreCase) &&
             x.PostalCode.Equals(addressModel.PostalCode, StringComparison.OrdinalIgnoreCase);

    // check if customer already has this address
    if (!customerAddresses.Any(addressFilter))
    {
        // check if address already exists in database
        var address = this.DbContext.Addresses.SingleOrDefault(addressFilter);

        // add address if it does not exist
        if (address == null)
        {
            address = this.DbContext.Addresses.Add(new Address
            {
                Street = addressModel.Street,
                City = addressModel.City,
                Province = addressModel.Province,
                PostalCode = addressModel.PostalCode
            });
        }
    }

    this.DbContext.CustomerAddresses.Add(new InsuredAddress
    {
        Customer = customer,
        Address = address,
        IsPreferred = addressModel.IsPreferred
    });             
}

然而,我担心每次在循环内创建Func<Address, bool> addressFilter。有没有办法以这样的方式创建过滤器,它接受参数而不需要每次都重新创建?

3 个答案:

答案 0 :(得分:2)

一旦理解了编译器如何处理匿名方法,就更容易看到这个含义。

编译器将创建一些具有任意名称的新时间。它将为该类型提供具有其他任意名称的实例方法。该方法的主体将有效地成为这种匿名方法的主体。

每个已关闭的变量都会有一个实例字段。

将在方法中创建此类型的新实例,并且将通过访问此闭包类的字段来替换已关闭的变量。对这个新类的调用将替换对匿名方法的调用。

因此,基于此转换,您应该能够看到匿名方法只编译一次,无论其在另一种方法中的定义范围如何。


说完所有这些之后,真的不应该出于完全无关的原因以这种方式构建你的程序。首先,您要定义Func而不是Expression<Func>,因此过滤器无法转换为在数据库上执行的查询,而是您正在下拉整个Addresses表格 两次(一次拨打Any时,一次拨打SingleOrDefault时,代表每个地址你的模型。那是真的坏。我的意思是,至少,你应该使用Expression来定义谓词,以便可以在事情的数据库方面,省略对Any的调用,这样你每个循环只进行一次查询,但老实说,你不应该在中执行多个查询。应该做的是加入两个表,以便在一个大查询中获取整个事物的所有信息,而不是在循环中执行任何查询。

答案 1 :(得分:0)

你在乎什么?

不要误解我的意思 - 但这只是一大堆物体的产生。实时问题将是db执行时间。所以,除非你能在这里描述一个问题,否则你很可能完全处于过早优化阶段。我会接受这样的代码。

答案 2 :(得分:0)

您可以使用列表(customerAddresses)和地址模型(addressModel)作为参数创建一个新方法,返回一个布尔值来替换&#34;任何&#34;。

private bool CheckSync(var customerAddresses, var addressModel)
{
    foreach(var item in customerAddresses)
    {
        if(item.Id != addressModel.Id 
        || !item.Street.Equals(addressModel.Street, StringComparison.OrdinalIgnoreCase)
        || !item.City.Equals(addressModel.City, StringComparison.OrdinalIgnoreCase)
        || !item.Province.Equals(addressModel.Province, StringComparison.OrdinalIgnoreCase)
        || !item.PostalCode.Equals(addressModel.PostalCode, StringComparison.OrdinalIgnoreCase))
        {
            return false;
        }
    }

    return true;
}

并且调用此方法而不是&#34;任何&#34;:

// check if customer already has this address
if (!CheckSync(customerAddresses, addressFilter))
{
    // ...