简体   繁体   中英

get all records between two dates using linq c# query

How to get all the records between two dates, below is my code. Can anyone help me out?

var query = from l in _DbContext.Licenses
                    join lp in _DbContext.LicenseParts on l.PartNumber equals lp.PartNumber
                    join p in _DbContext.Products on lp.ProductId equals p.Id
                    join lsn in _DbContext.LicenseSerialNumbers on l.Id equals lsn.LicenseId
                    join lact in _DbContext.LicenseActivations on lsn.Id equals lact.LicenseSerialNumberId
                    join bpsn in _DbContext.BasePartSerialNumbers on JsonExtensions.JsonValue(lact.ActivationInfoJSON, "$.SerialNumber") equals bpsn.SerialNumber into bpsnOuter
                    from bpsnO in bpsnOuter.DefaultIfEmpty()
                    join bp in _DbContext.BaseParts on bpsnO.BasePartId equals bp.Id into bpOuter
                    from bpO in bpOuter.DefaultIfEmpty()
                    join lpc in _DbContext.LicensePartConfigurations on
                    new
                    {
                      Key1 = lp.Id,
                      Key2 = JsonExtensions.JsonValue(lact.ActivationInfoJSON, "$.Version")
                    }
                    equals
                    new
                    {
                      Key1 = lpc.LicensePartId,
                      Key2 = lpc.Version
                    }
                    into lpcOuter
                    from lpcO in lpcOuter.DefaultIfEmpty()
                    where lp.Active == true && lact.Status == Common.LicenseActivationStatus.ACTIVE
                    select new ActivatedLicenseQueryInfo
                    {
                      p = p,
                      lact = lact,
                      version = lpcO == null ? "" : lpcO.Version,
                      bPart = bpO == null ? "" : bpO.PartNumber,
                    };
        availableOrderRequest.Products = Array.ConvertAll(availableOrderRequest.Products, x => x.ToUpper());
        query = query.Where(o => availableOrderRequest.Products.Contains(o.p.Name.ToUpper()));
        query = query.Where(o => o.l.CreatedAt >= Convert.ToDateTime(availableOrderRequest.DateRangeFrom) && o.l.CreatedAt <= Convert.ToDateTime(availableOrderRequest.DateRangeTo));

In DB CreatedAt column date's like 2010-09-10 10:17:28.8133333 formate, and input DateRangeFrom and DateRangeTo inputs like "2021-04-01" but I am getting an error:

the problem is this line

        query = query.Where(o => o.l.CreatedAt >= Convert.ToDateTime(availableOrderRequest.DateRangeFrom) && o.l.CreatedAt <= Convert.ToDateTime(availableOrderRequest.DateRangeTo));

without this line it's working fine, if I add this line I am getting the below error

Message [string]:"The LINQ expression 'DbSet\n.Join(\n outer: DbSet, \n inner: l => l.PartNumber, \n outerKeySelector: l0 => l0.PartNumber, \n innerKeySelector: (l, l0) => new TransparentIdentifier<License, LicensePart>(\n Outer = l, \n Inner = l0\n ))\n.Join(\n outer: DbSet, \n inner: ti => ti.Inner.ProductId, \n outerKeySelector: p => p.Id, \n innerKeySelector: (ti, p) => new TransparentIdentifier<TransparentIdentifier<License, LicensePart>, Product>(\n Outer = ti, \n Inner = p\n ))\n.Join(\n outer: DbSet, \n inner: ti0 => ti0.Outer.Outer.Id, \n outerKeySelector: l1 => l1.LicenseId, \n innerKeySelector: (ti0, l1) => new TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<License, LicensePart>, Product>, LicenseSerialNumber>(\n Outer = ti0, \n Inner = l1\n ))\n.Join(\n...

The cause of your problem

Although an IQueryable<...> looks similar to an IEnumerable<...> , there is a major difference.

Both objects reprresent an enumerable sequence of similar object. An object that implements IEnumerable, is supposed to be enumerated by your local process. It holds everything to get the first element of the sequence, and if you've got an element, it can get the next element of the sequence as long as there is a next element.

At low level, enumerating the sequence is done as follows:

IEnumerable<Product> products = ...
using (IEnumerator<Product> productEnumerator = products.GetEnumerator())
{
    while (productEnumerator.MoveNext())
    {
        Product product = productEnumerator.Current;
        this.Process(product);
    }
}

Whenever you use foreach, then deep down these statements are executed. The LINQ methods that do not return IEnumerable<...> , like ToList, FirstOrDefault, Count, Any, etc, will also deep inside call GetEnumerator / MoveNext / Current

Although an object that implements IQueryable also represents an enumerable sequence of similar objects, it is a bit different. The IQueryable holds an Expression and a Provider . The Expression represents the data that must be fetched. The Provider knows which process will provide the data (usually a database management system), and what language is used to communicate with this DBMS (usually SQL).

If you call IQueryable.GetEnumerator , the Expression is sent to the Provider, who will translate it into SQL and execute the request at the DBMS. The fetched data is represented as an IEnumerator<...> to the caller, who can repeatedly call MoveNext / Current, to enumerate the fetched data.

Some Providers are smart, and will delay fetching the data until the first MoveNext. Others are even smarter: they will not fetch all data at once, but "per page", of say, the first 25 elements. If you only enumerate the first few elements (like FirstOrDefault), then not all data is fetched for nothing. After these 25 elements are enumerated, the next page is fetched.

The problem is, that your Provider does not know how to translate Convert.ToDateTime(...) into SQL. In fact, there are even several LINQ methods that are not supported by your Provider. See List of Supported and Usupported LINQ methods .

Although you didn't say so, it seems to me that object availableOrderRequest is an object in your local process. This object has at least two (string?) properties DateRangeFrom and DateRangeTo.

My advice would be to let your local process convert these properties to DateTime before you execute the query.

var availableOrderRequest = ...
DateTime dateRangeFrom = Convert.ToDateTime(availableOrderRequest.DateRangeFrom);
DateTime dateRangeTo = Convert.ToDateTime(availableOrderRequest.DateRangeTo);

var query1 = from l in dbContext.Licenses ...
var query2 = query1.Where(o => availableOrderRequest.Products.Contains(o.p.Name.ToUpper()));
var query3 = query2.Where(o => o.l.CreatedAt >= dateRangeFrom && o.l.CreatedAt <= dateRangeTo);

This looks much neater, and is better to read. What is more important, is, that it is also more efficient, as the Convert.ToDateTime(...) is executed only once, not once per o in the Where .

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