Getting first free Id from DB - c#

Very often my code is returned on code review by other colleagues. I need explanation why second code is better than first.
Point is to get first empty Device Type Id from database in range predefined.
Device Type Ids can be:[1,2,4,10,11,12,14]
So if a range is from 10 to 20 predefined I want to get 13 because its first free Id.
So here is first code that do what it should:
firstFreeId = Enumerable.Range(_idRangeFrom.Value + 1, _idRangeTo.Value).First(i => !_dbContext.DeviceTypes.Any(dt => dt.DeviceTypeId == i));
Where _idRangeFrom and _idRangeTo are predefined ranges and _dbContext.DeviceTypes is a EF db Context that is a context of DB table DeviceTypes
Second code is:
var deviceTypeIds = _dbContext.DeviceTypes.Where(e => e.DeviceTypeId > _idRangeFrom.Value && e.DeviceTypeId < _idRangeTo.Value).Select(e => e.DeviceTypeId);
firstFreeId = Enumerable.Range(_idRangeFrom.Value + 1, _idRangeTo.Value).First(i => !deviceTypeIds.Any(dtId => dtId == i));
So my question is what I'm exactly getting with second code that I dont have in first?

Related

Using C# Entity Framework find a value based on 2 high low column values

If the input value is between MinIncome and MaxIncome in my database table, I want to find the Amount db field value. So for instance if an input was 1525 falling between 1500 & 1599, I would expect a return value of 75.
How do I write a Linq or Lambda statement for this? Please see screen shot.
int income = 1525;
int amount = dbContext
.MyDbSet // use DbSet Property here
.Single(x => x.MinIncome < income && x.MaxIncome > income)
.Amount;
This selects the only element where the condition is met, and returns the amount of that object.
You could also use .Where(x => x.MinIncome < income && x.MaxIncome > income).Single().Amount to achieve the exact same result.
Other options include .SingleOrDefault() which prevents errors in case no entry in your DbSet matches, .First() which prevents errors if multiple entries match, or .FirstOrDefault() if no entries or multiple entries can match.
Try this:
var result = await DbContext
.Table
.Where(x => x.MinIncome <= value && value <= x.MaxIncome)
.Select(x => x.Amount).FirstOrDefaultAsync();

Replacing Include() calls to Select()

Im trying to eliminate the use of the Include() calls in this IQueryable definition:
return ctx.timeDomainDataPoints.AsNoTracking()
.Include(dp => dp.timeData)
.Include(dp => dp.RecordValues.Select(rv => rv.RecordKind).Select(rk => rk.RecordAlias).Select(fma => fma.RecordAliasGroup))
.Include(dp => dp.RecordValues.Select(rv => rv.RecordKind).Select(rk => rk.RecordAlias).Select(fma => fma.RecordAliasUnit))
.Where(dp => dp.RecordValues.Any(rv => rv.RecordKind.RecordAlias != null))
.Where(dp => dp.Source == 235235)
.Where(dp => dp.timeData.time >= start && cd.timeData.time <= end)
.OrderByDescending(cd => cd.timeData.time);
I have been having issues with the database where the run times are far too long and the primary cause of this is the Include() calls are pulling everything.
This is evident in viewing the table that is returned from the resultant SQL query generated from this showing lots of unnecessary information being returned.
One of the things that you learn I guess.
The Database has a large collection of data points which there are many Recorded values.
Each Recorded value is mapped to a Record Kind which may have a Record Alias.
I have tried creating a Select() as an alternative but I just cant figure out how to construct the right Select and also keep the entity hierarchy correctly loaded. I.e. the related entities are loaded with unnecessary calls to the DB.
Does anyone has alternate solutions that may jump start me to solve this problem.
Ill add more detail if needed.
You are right. One of the slower parts of a database query is the transport of the selected data from the DBMS to your local process. Hence it is wise to limit this.
Every TimeDomainDataPoint has a primary key. All RecordValues of this TimeDomainDataPoint have a foreign key TimeDomainDataPointId with a value equal to this primary key.
So If TimeDomainDataPoint with Id 4 has a thousand RecordValues, then every RecordValue will have a foreign key with a value 4. It would be a waste to transfer this value 4 a 1001 times, while you only need it once.
When querying data, always use Select and select only the properties you actually plan to use. Only use Include if you plan to update the fetched included items.
The following will be much faster:
var result = dbContext.timeDomainDataPoints
// first limit the datapoints you want to select
.Where(datapoint => d.RecordValues.Any(rv => rv.RecordKind.RecordAlias != null))
.Where(datapoint => datapoint.Source == 235235)
.Where(datapoint => datapoint.timeData.time >= start
&& datapoint.timeData.time <= end)
.OrderByDescending(datapoint => datapoint.timeData.time)
// then select only the properties you actually plan to use
Select(dataPoint => new
{
Id = dataPoint.Id,
RecordValues = dataPoint.RecordValues
.Where(recordValues => ...) // if you don't want all RecordValues
.Select(recordValue => new
{
// again: select only the properties you actually plan to use:
Id = recordValue.Id,
// not needed, you know the value: DataPointId = recordValue.DataPointId,
RecordKinds = recordValues.RecordKinds
.Where(recordKind => ...) // if you don't want all recordKinds
.Select(recordKind => new
{
... // only the properties you really need!
})
.ToList(),
...
})
.ToList(),
TimeData = dataPoint.TimeData.Select(...),
...
});
Possible imporvement
The part:
.Where(datapoint => d.RecordValues.Any(rv => rv.RecordKind.RecordAlias != null))
is used to fetch only datapoints that have recordValues with a non-null RecordAlias. If you are selecting the RecordAlias anyway, consider doing this Where after your select:
.Select(...)
.Where(dataPoint => dataPoint
.Where(dataPoint.RecordValues.RecordKind.RecordAlias != null)
.Any());
I'm not really sure whether this is faster. If your database management system internally first creates a complete table with all columns of all joined tables and then throws away the columns that are not selected, then it won't make a difference. However, if it only creates a table with the columns it actually uses, then the internal table will be smaller. This could be faster.
your problem is hierarchy joins in your query.In order to decrease this problem create other query for get result from relation table as follows:
var items= ctx.timeDomainDataPoints.AsNoTracking().Include(dp =>dp.timeData).Include(dp => dp.RecordValues);
var ids=items.selectMany(item=>item.RecordValues).Select(i=>i.Id);
and on other request to db:
var otherItems= ctx.RecordAlias.AsNoTracking().select(dp =>dp.RecordAlias).where(s=>ids.Contains(s.RecordKindId)).selectMany(s=>s.RecordAliasGroup)
to this approach your query do not have internal joins.

LINQ checking if an ICollection<int> has any crossover with c# array

So i have a c# array that contains integers.
In my database i have an entity called request. Request has a one to many relationship with a table called Weeks. In weeks there is a requestID and a week (so week 1 would have value 1, week 2 -> 2 etc.).
I need to construct a where clause that if the database record has any sort of overlap with my c# array, it gets that record.
So for instance, say a request has weeks 1,2,3 connected to it in the database, and my c# array has [3,5,6], i want that request to be pulled.
Any ideas of how to get this done?
this is my current LINQ code so far
IQueryable<request> proposedRequest = db.requests.Include(r => r.rooms);
proposedRequest = proposedRequest.Where(r=>r.booked.Equals(1));
proposedRequest = proposedRequest.Where(r => r.roundID.Equals(roundID));
proposedRequest = proposedRequest.Where(r=>r.day.Equals(day));
proposedRequest = proposedRequest.Where(s=>s.start<time+length && s.start + s.length > time);
//I attempted something here but obviously it doesnt work
proposedRequest = proposedRequest.Where(r=>weeks.Contains(r.weeks_request.Any(f=>f.week)));
so in my request table there is a attribute weeks. This is either 1 or 0. If it is 1, this means the request is for a standard set of weeks, which is 1-12. This means that there is not an entry in the weeks_request table for this request.
If the weeks attribute is 0, this means that it is any permutation of weeks 1-16, not not the standard 1-12. Therefore there is an individual entry in the weeks_request table for each week selected.
The array weeks contains the weeks that the user wants to book for. But obviously they cannot book a week if it is already booked. Therefore, i need to find if it is already booked.
Any ideas?
Try changing:
r=>weeks.Contains(r.weeks_request.Any(f=>f.week))
To:
r=>r.weeks_request.Any(f=>weeks.Contains(f.week))
Update based on updated question
I am not entirely sure I follow what you have going on but try this query:
IQueryable<request> proposedRequest =
db.requests.Include(r => r.rooms)
.Where(r =>
r.booked.Equals(1) &&
r.roundID.Equals(roundID) &&
r.day.Equals(day) &&
r.start < time + length &&
r.start + r.length > time &&
((r.week == 0 && weeks.Any(w => w <= 12)
|| (r.week == 1 && r.weeks_request.Any(w => weeks.Contains(w.week));

Identifying Date Clashes using linq

I am looking to identify rows using linq where there is a date clash. I have (for this example) 5 columns
ID ref_id ref_Name Borrow_Date Return_Date
1 1343 Gate 13/09/2011 20/09/2011
2 1352 Door 20/09/2011 22/09/2011
3 1343 Gate 17/09/2011 21/09/2011
In this case my 'Gate' is clashing because someone wants to borrow it when someone else also wants to borrow it.
Is there anyway to identify the date range clashes using linq easily?
One way would be like this. It might be more performant variants out there though:
var collisions = myList.Where( d1 => !myList.Where( d => d != d1).All( d2 => d1.Return_Date <= d2.Borrow_Date|| d1.Borrow_Date >= d2.Return_Date));
This will return all rows that overlap with at least one other row. In the case above it will return all three of them, since the line with ID 3 overlaps both 1 and 2. If you change 1 to have Return_Date 17/09/2011, it will return only 2 and 3.
If you have a list of objects with properties as shown in your table, you can find out the books with the same title that have conflicting dates using something like this:
(Haven't tested this code, so there might be some typo bugs.)
var collisions = collection
.Join(collection, x => x.ref_Name, y => y.ref_Name,
(x, y) => new {
ID_x = x.ID,
ID_y = y.ID,
ref_id = x.ref_id,
ref_Name = x.ref_Name,
Borrow_Date_x = x.Borrow_Date,
Borrow_Date_y = y.Borrow_Date,
Return_Date_x = x.Return_Date,
Return_Date_y = y.Return_Date
}
)
.Where( z => (z.Return_Date_x > z.Borrow_Date_y && z.Borrow_Date_x < z.Return_Date_y))
.Where( z => z.ID_x != z.ID_y);
You will probably get duplicates of results. (i.e. ID 1 and 3, and ID 3 and 1)
Although it is certainly possible to identify these clashes in the database once they have occurred, would it not be a better to prevent the second person from borrowing an item when it is already scheduled to be borrowed. In this case this would be a simple matter of testing to ensure no existing rows with a ref_id of 1343 have a return date equal to or greater then the new requested borrow date.

Linq Union: How to add a literal value to the query?

I need to add a literal value to a query. My attempt
var aa = new List<long>();
aa.Add(0);
var a = Products.Select(p => p.sku).Distinct().Union(aa);
a.ToList().Dump(); // LinqPad's way of showing the values
In the above example, I get an error:
"Local sequence cannot be used in LINQ to SQL implementation
of query operators except the Contains() operator."
If I am using Entity Framework 4 for example, what could I add to the Union statement to always include the "seed" ID?
I am trying to produce SQL code like the following:
select distinct ID
from product
union
select 0 as ID
So later I can join the list to itself so I can find all values where the next highest value is not present (finding the lowest available ID in the set).
Edit: Original Linq Query to find lowest available ID
var skuQuery = Context.Products
.Where(p => p.sku > skuSeedStart &&
p.sku < skuSeedEnd)
.Select(p => p.sku).Distinct();
var lowestSkuAvailableList =
(from p1 in skuQuery
from p2 in skuQuery.Where(a => a == p1 + 1).DefaultIfEmpty()
where p2 == 0 // zero is default for long where it would be null
select p1).ToList();
var Answer = (lowestSkuAvailableList.Count == 0
? skuSeedStart :
lowestSkuAvailableList.Min()) + 1;
This code creates two SKU sets offset by one, then selects the SKU where the next highest doesn't exist. Afterward, it selects the minimum of that (lowest SKU where next highest is available).
For this to work, the seed must be in the set joined together.
Your problem is that your query is being turned entirely into a LINQ-to-SQL query, when what you need is a LINQ-to-SQL query with local manipulation on top of it.
The solution is to tell the compiler that you want to use LINQ-to-Objects after processing the query (in other words, change the extension method resolution to look at IEnumerable<T>, not IQueryable<T>). The easiest way to do this is to tack AsEnumerable() onto the end of your query, like so:
var aa = new List<long>();
aa.Add(0);
var a = Products.Select(p => p.sku).Distinct().AsEnumerable().Union(aa);
a.ToList().Dump(); // LinqPad's way of showing the values
Up front: not answering exactly the question you asked, but solving your problem in a different way.
How about this:
var a = Products.Select(p => p.sku).Distinct().ToList();
a.Add(0);
a.Dump(); // LinqPad's way of showing the values
You should create database table for storing constant values and pass query from this table to Union operator.
For example, let's imagine table "Defaults" with fields "Name" and "Value" with only one record ("SKU", 0).
Then you can rewrite your expression like this:
var zero = context.Defaults.Where(_=>_.Name == "SKU").Select(_=>_.Value);
var result = context.Products.Select(p => p.sku).Distinct().Union(zero).ToList();

Categories