C# 在循环内优化LINQ查询

C# 在循环内优化LINQ查询,c#,linq,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

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

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
    });             
}
var customeraddress=customer.customeraddress.Select(x=>x.Address);
//如果客户地址不存在,则向其添加地址
foreach(model.Addresses中的var addressModel)
{
//确保地址与所有属性匹配
Func addressFilter=x=>x.Id==addressModel.Id&&
x、 Street.Equals(addressModel.Street,StringComparison.OrdinalIgnoreCase)&&
x、 City.Equals(addressModel.City,StringComparison.OrdinalIgnoreCase)&&
x、 等于(addressModel.Province,StringComparison.OrdinalIgnoreCase)&&
x、 PostalCode.Equals(addressModel.PostalCode,StringComparison.OrdinalIgnoreCase);
//检查客户是否已拥有此地址
如果(!customerAddresses.Any(addressFilter))
{
//检查数据库中是否已存在地址
var address=this.DbContext.Addresses.SingleOrDefault(addressFilter);
//如果地址不存在,请添加地址
如果(地址==null)
{
address=this.DbContext.Addresses.Add(新地址
{
街道=地址模型。街道,
城市=地址模型。城市,
省=地址模型。省,
PostalCode=addressModel.PostalCode
});
}
}
this.DbContext.CustomerAddresses.Add(新保险地址
{
客户=客户,
地址,
IsPreferred=addressModel.IsPreferred
});             
}
但是,我担心每次在循环中都会创建
Func addressFilter
。是否有一种方法可以创建过滤器,使其接受参数而不需要每次重新创建?

您关心什么


别误会我的意思——但这只是一堆对象的生成。实时问题将是数据库执行时间。所以,除非你能在这里描述一个问题,否则你很可能完全处于过早优化状态。我会接受原样的代码。

一旦您了解了编译器是如何处理匿名方法的,就更容易理解这一点的含义

编译器将使用任意名称创建一些新时间。它将为该类型提供一个具有其他任意名称的实例方法。该方法的主体实际上就是这个匿名方法的主体

每个闭合变量都有一个实例字段

此类型的新实例将在该方法中创建,并且closed over变量将替换为对此闭包类字段的访问。对匿名方法的调用将替换为此新类中的方法的调用

因此,基于此转换,您应该能够看到匿名方法只编译了一次,而与它在另一个方法中的定义范围无关



说到这里,你真的不应该因为完全无关的原因而以这种方式构建你的程序。首先,您定义的是
Func
而不是
表达式,因此筛选器无法转换为在数据库上执行的查询,而是将整个
地址
下拉两次(一次当你调用
Any
,一次当你调用
SingleOrDefault
,对你模型中的每个地址。那真的很糟糕。我的意思是,至少你应该使用
表达式来定义谓词,这样就可以在数据库端对事物进行过滤,而忽略它g调用
Any
,这样每个循环只执行一个查询,但老实说,您根本不应该执行多个查询。您应该做的是将两个表合并,以便在一个大查询中获得整个查询的所有信息,而不是在一个循环中执行任何查询。

您可以创建一个新的Me将列表(customerAddresses)和地址模型(addressModel)作为参数的thod返回一个布尔值以替换“Any”

调用此方法而不是“Any”:


你不能在循环外声明委托吗?@Magus不,因为它依赖于循环变量。啊,这更有意义。那么,我可能只需要为循环变量的委托添加另一个参数。只是想知道,你不能让你的address类实现一个IComparable/IEquatable接口并进行比较吗也许是这样?他不能,因为该函数被转换成sql。我认为
Any
比循环有更多的好处?您现在已经完全消除了在数据库上执行此查询的任何可能性,并强制在内存中执行。而且,这实际上并没有回答所问的问题。@vandango它返回true If序列中至少有一项谓词为真。我也这么认为,但后来我想……问起来没什么坏处。你在这里提出了非常有趣的观点。我真的想:(1)A
Func
也会被翻译,和(2)
Any
是个好主意。我喜欢你关于
JOIN
的建议,但我不知道该怎么做。想提供一个例子吗?或者链接吗?这太离题了,无法深入到我已经掌握的更多细节。不过,你可以通过谷歌找到很多关于如何连接表的信息;有大量的信息队形在那边。
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;
}
// check if customer already has this address
if (!CheckSync(customerAddresses, addressFilter))
{
    // ...