我如何在LINQ中使用带有条件的计数

问题描述

我想获得客户参与我们活动的次数。因此,sql代码应该是这样的。

SELECT COUNT(CustomerID)
FROM EventsParticipants
WHERE (CustomerID == Session["CustomerID"]);

LINQ代码是这样的,我没有记错,它将返回 Count()值。

var recordcount = db.EventParticipants.Where(Session["CustomerID] == db.EventParticipants.CustomerID).Count();

但是它返回此错误代码

'DbSet<EventParticipants>' does not contain a deFinition for 'CustomerID' and no accessible extension method 'CustomerID' accepting a first argument of type 'DbSet<EventParticipants>' Could be found (are you missing a using directive or an assembly reference?)    LeafLife

解决方法

您需要传递一个包含实体并返回bool的lambda,像这样

var id = Session["CustomerID"];
var recordcount = db.EventParticipants.Where(x => x.CustomerID == id).Count();

请注意,由于EF无法将Session转换为SQL,您还希望将id放在一个单独的变量中。

,

在EF Linq查询中使用变量之前,需要将Session["CustomerID"]值分配给变量。

此外,您不需要过滤然后获得计数,您可以直接在LINQ计数函数中提及条件。请参考以下

var id = Convert.ToInt32(Session["CustomerID"]); // Assuming this as int
var recordcount = db.EventParticipants.Count(x => x.CustomerID == id);
,

尝试以下方法:

var id = Session["CustomerID"];    
var recordcount = db.EventParticipants.Count(t=> id == t.CustomerID);

稍后再执行Count时,它将在获得所有结果后计入内存。本质上,它将执行SELECT * FROM EventsParticipants WHERE (CustomerID == Session["CustomerID"]);,然后在内存中查找其中有多少项。 (不要这样做)

如果您执行上述操作,将创建与您期望的查询相同的查询,并且查询速度更快。

,

Lambda表达式

只要在LINQ中看到Func<...>Expressions<Func<...>>格式的参数,就需要为函数提供正确的输入参数和正确的返回值。

Func<Ta,Tb,Tc>表示具有两个输入参数和一个返回值的任何方法。第一个参数的类型为Ta,第二个参数的类型为Tb。返回值是Tc类型。可以使用以下方法:

bool String.Equals(string x,string y);
Shop FindShop(Product product,decimal maximumPrice);

当然,这些类型必须与通用LINQ方法中的其他类型匹配。

例如,如果您有CustomersOrders,并且想要创建PackingSlips,则可能要为此使用Enumerable.Join

public static IEnumerable<TResult> Join<TOuter,TInner,TKey,TResult> (
    this IEnumerable<TOuter> outer,IEnumerable<TInner> inner,Func<TOuter,TKey> outerKeySelector,Func<TInner,TKey> innerKeySelector,TResult> resultSelector);

TOuter的实际类型为CustomerTInner的实际类型为Order。要产生结果,请使用参数resultSelector:

Func<TOuter,TResult> resultSelector

在您的情况下:

Func<Customer,Order,PackingSlip> resultSelector

这意味着,您必须提供一个具有两个输入参数的函数:Customer和Order,并返回一个PackingSlip:

PackingSlip CreatePackingSlip(Customer customer,Order order);

因此,您将按以下方式使用Join:

IEnumerable<Customer> customers = ...
IEnumerable<Order> orders = ...
IEnumerable<PackingSlip> packingSlips = customers.Join(orders,... // parameter keySelector,see later,// parameter resultSelector:
    CreatePackingSlip);

可以,但是使用起来并不方便,因此不会经常使用。例如,如果您具有以下方法怎么办:

PackingSlip CreatePackingSlip(Order order,Customer customer);

为此创建了lambda表达式。其格式为:

(...) => ...

这意味着:创建一个Func<...>或Action

Func<Customer,PackingSlip> resultSelector

(customer,order) => CreatePackingSlip(order,customer);

与以下相同:

PackingSlip CreatePackingSlip(Customer customer,Order order)
{
    return CreatePackingSlip(order,customer); // call the other overload
}

但是实际上您可以为此使用任何代码块:

(customer,order) => new PackingSlip()
                     {
                          Customer = customer,Order = Order,});

甚至一小段代码:

(customer,order) => 
{
    string addressLines = this.CreateAddressLines(customer);
    var orderLines = this.CreateOrderLines(order);
    decimal totalPrice = this.CalculateTotalPrice(order);

    PackingSlip packingSlip = new PackingSlip()
    {
       Address = addressLines,OrderLines = orderLines,TotalPrice = totalPrice,};
    return packingSlip;
});

任何事情都可以,只要括号(...,...)之间的部分以正确的顺序提及Func<...>的参数,并且=>之后的部分返回的返回值即可。功能。

我答应了上面Join的参数keySelector。它们用于指定您要加入的内容。在这种情况下:我们想将主键Customer.Id与外键Order.CustomerId匹配:

IEnumerable<Customer> customers = ...
IEnumerable<Order> orders = ...

IEnumerable<PackingSlip> packingSlips = customers.Join(orders,customer => customer.Id,// from every customer take the primary key in Id
    order => order.CustomerId,// from every order take the foreign key CustomerId
    
    // parameter resultSelector take the Customer and its matching Order
    // to make one new PackingSlip:
    (customer,order) => new PackingSlip()
    {
       Address = this.CreateAddressLines(customer),OrderLines = this.CreateOrderLines(order),TotalPrice = this.CalculateTotalPrice(order),});

使用LINQ时,使用复数名词来命名项目集合以及使用单数名词来命名集合元素是有帮助的。

因此,如果您加入customersorders(集合!),则lambda表达式应使用

// Get Schools,each with their Students:
schools.GroupJoin(students
school => school.Id,student => student.SchoolId,(school,studentsOfThisSchool) => new {...})

schools.GroupJoin(students
    x => x.Id,y => y.SchoolId,(z,a) => new {...})

您确实必须查看代码以了解a中的内容。

回到您的问题

显然,您有一个包含EventParticipants的表,其中每个EventParticipant都有一个属性CustomerId。您只想获取特定值为CustomerId的EventParticipant。

int requestedCustomerId = Session["CustomerId"];
var result = dbContext.EventParticipants.
    .Where(eventParticipant => eventParticipant.CustomerId == requestedCustomerId);

因为我使用了复数形式和单数形式的名词,所以很容易理解:“从EventParticipant表中的每个eventParticipant中,请检查其属性CustomerId的值是否等于requestedCustomerId。不要使用它。

现在,您要计算CustomerIds之后剩余的Where。假设每个EventParticipant都有一个CustomerId,则可以计算EventParticipant:

var eventParticipantCount = dbContext.EventParticipants.
    .Where(eventParticipant => eventParticipant.CustomerId == requestedCustomerId)
    .Count();

但是,如果您确实要计算CustomerId,则首先必须选择它们。如果您期望重复的CustomerId,这将很有用:

var uniqueCustomerIdCount = dbContext.EventParticipants.
    .Where(eventParticipant => eventParticipant.CustomerId == requestedCustomerId)

    // from every remaining eventParticipant select its CustomerId
    .Select(eventParticipant => eventParticipant.CustomerId)

    // remove duplicate CustomerIds:
    .Distinct()

    // and count what is left:
    .Count();