简体   繁体   中英

Replace part of a LINQ to SQL query with a reusable method

We have introduced a new feature in our application, that affects hundreds of queries. We have to set a bool field to indicate if the license is valid, in a very complicated way.

I would like to create a method for returning this bool value, and I'd like to use it in every query. The problem is, that if I use it in the way shown below, it executes a separate query for each result.

How do I use the Expression in a way that it is compiled into SQL and executed as a single query?

Original query, in need of improvement

IQueryable<DeviceMinimal> devices = 
    from device in db.Devices
    where device.AccountId = accountId

    select new DeviceMinimal
    {
        Id = device.Id,
        Name = device.Name,
        LicenseIsValid = !checkForLicense || 
            device.License != null && (
                !device.License.TrialStarted
                // && 12+ licensing rules
            )
    };

checkForLicense is a bool that indicates that license does not need to be checked. It is used in some cases and it is necessary to be considered.

Code, that solves the problem, but provokes a separate query for each device

IQueryable<DeviceMinimal> devices = 
    from device in db.Devices
    where device.AccountId = accountId

    select new DeviceMinimal
    {
        Id = device.Id,
        Name = device.Name,
        LicenseIsValid = 
            LicenseHelper.IsLicenseValid(checkForLicense).Invoke(device)
    };

The method use in the query above:

public static Func<Device, bool> IsLicenseEnabledAndValid(bool checkForLicense)
{
    return result => !checkForLicense ||                 
        result.License != null && (
            !result.License.TrialStarted
            // && 12+ licensing rules
        );
}

If you set the DataLoadOptions of the DataContext before the query and setup them right, you should avoid the subselect. Something similar to this:

db.LoadOptions.LoadWith<Devices>(p => p.License);

Tha is the default behaviour (lazy loading of the entities). You could have for more info searching for 'linq to sql eagerloading'

Not sure if it will work, but did you try accessing the licence in the main query? In other words something like :

Queryable<DeviceMinimal> devices =
   from device in db.Devices
   where device.AccountId = accountId

   select new DeviceMinimal
   {
       Id = device.Id,
       Name = device.Name,
       LicenseIsValid =
        LicenseHelper.IsLicenseEnabledAndValid(checkForLicense).Invoke(device.Licence)
   };

public static Func<License, bool> IsLicenseEnabledAndValid(bool checkForLicense)
{
    return result => !checkForLicense ||
        result != null && (
       !result.TrialStarted
        // && 12+ licensing rules
      );
}

If you need to access both the device and Licence in your method, you might need to change the function to be something like

public static Func<Device, License, bool> IsLicenseEnabledAndValid(bool checkForLicense)
{
   return (device, licence) =>  
  ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM