Query using EF "Include" and including the related tables on Where - linq

So far, I thought I could do:
var num = db.MyTable.Include(x => x.RelatedTable)
.Count( x.idTenant == CurrentTenantID && x.Active &&
x.RelatedTable.SomeProperty.Value == true);
This always return zero records.
Am I assuming wrongly that Including the RelatedTable I can use it in the where part?
By the way... the "SomeProperty" is Nullable, that is why the ".Value".
I'm using Entity Framework 4.1. (Database first)

Are you trying to get the number of records? If so, why do you even need the Include? Entity Framework will lazy-load the RelatedTable entity set for you when it evaluates your Count condition. Also, if SomeProperty is a bool?, you should check if it has a value before you check the value itself.
var num = db.MyTable.Count(x =>
x.idTenant == CurrentTenantID &&
x.Active &&
(x.RelatedTable.SomeProperty.HasValue &&
x.RelatedTable.SomeProperty.Value));

You don't need to use Include if you only want to access navigation property in Where part. Include is only used to fetch (eager load) related records together with the main record from database to your application but it doesn't make sense if you only want to count records.

Related

Update one column and set all values to 1 using Entity Framework

how does one update 1 column to set the value to one for all rows, so my table has three columns customerno,name,doneflag, I want to set the Doneflag to 1 for all,currently i have 8 rows in my table and all have a value of 0 as Doneflag, I want to do one update to set the doneflag for all rows to 1 using entity framework, this is simple using Mysql as it would be:
update myDB.Customer
set doneflag = 0;
I tried this but does not work;
context.Customer.Add(x => x.Doneflag = 1);
You need to load the existing records, modify them and then save the changes, e.g.
var notYetDoneCustomers = await context.Customers.Where(c => c.DoneFlag == 0).ToListAsync();
foreach(var cust in notYetDoneCustomers) {
cust.DoneFlag = 1;
}
await context.SaveChangesAsync();
The standard entity framework flow is
Load data
Modify it
Save changes
For bulk updates, you have several options:
Use the standard flow
Use an add-on library, see: Entity Framework Core(7) bulk update
Use SQL: https://learn.microsoft.com/en-us/ef/core/querying/raw-sql
This topic is covered in the docs here: https://learn.microsoft.com/en-us/ef/core/performance/efficient-updating
Unfortunately, EF doesn't currently provide APIs for performing bulk updates. Until these are introduced, you can use raw SQL to perform the operation where performance is sensitive:
Using a library like entityframework plus has the benefit, that you stay in a type-safe world that you would loose if you would perform raw SQL.
Another alternative:
Install linq2db.EntityFrameworkCore (disclaimer: I'm one of the creators)
context.Customers
.Where(c => c.doneFlag == 0)
.Set(c => c.doneflag, 1)
.Update();
In EF 6 you can use await myDB.Database.ExecuteSqlRawAsync or myDB.Database.ExecuteSqlRaw for UPDATE, INSERT OR DELETE SQL command valid expression, example:
var commandText = "UPDATE Customer SET doneflag = 0";
await _context.Database.ExecuteSqlRawAsync(commandText);
This will update all records in Costumer to doneflag = 0. Remember parameterize user input to prevent the possibility of a SQL injection attack being successful.

LINQ Check for Nulls with OR

I have 2 values in table User: Address1, Address2. Both could be null. As part of a filter method, I am attempting something like the below:
var tempUsers = users.Where(q => q.Address1.ToLower().Contains(address.ToLower()) || q.Address2.ToLower().Contains(address.ToLower()));
This is returning a Null Reference Exception, and rightly so.
Linq queries need to be handled against null values
I would be attempting
null.ToLower() and null.Contains() within the query
What is the best way to go around it? If it was a simple 1 field Query, for e.g. just Address1, I would have simply filtered out all items with empty Address1, and continued normally in the second query. In this case, both fields are important to the filtering, as in, the input: address could be either in Address1 or Address2 of the User table.
I know this might not be possible in a 1 liner, but what is the best approach to take in terms of time and performance?
How about this:
var address = (GetAddressFromOuterWorld() ?? String.Empty).ToLower();
var tempUsers = users.Where(user => (user.Address1 ?? String.Empty).ToLower().Contains(address)
|| (user.Address2 ?? String.Empty).ToLower().Contains(address));
This definitely works with LINQ to Object, but probably fails with LINQ to SQL, but in that case you normally write user.Address1 == address || user.Addrss2 == address and your database uses a case-insensitive collate setting.
You can easily add null checks like this.
var tempUsers = users.Where(q =>
(!string.IsNullOrEmpty(q.Address1) && q.Address1.ToLower().Contains(address.ToLower())) ||
(!string.IsNullOrEmpty(q.Address2) && q.Address2.ToLower().Contains(address.ToLower())));

NHibernate IQueryable doesn't seem to delay execution

I'm using NHibernate 3.2 and I have a repository method that looks like:
public IEnumerable<MyModel> GetActiveMyModel()
{
return from m in Session.Query<MyModel>()
where m.Active == true
select m;
}
Which works as expected. However, sometimes when I use this method I want to filter it further:
var models = MyRepository.GetActiveMyModel();
var filtered = from m in models
where m.ID < 100
select new { m.Name };
Which produces the same SQL as the first one and the second filter and select must be done after the fact. I thought the whole point in LINQ is that it formed an expression tree that was unravelled when it's needed and therefore the correct SQL for the job could be created, saving my database requests.
If not, it means all of my repository methods have to return exactly what is needed and I can't make use of LINQ further down the chain without taking a penalty.
Have I got this wrong?
Updated
In response to the comment below: I omitted the line where I iterate over the results, which causes the initial SQL to be run (WHERE Active = 1) and the second filter (ID < 100) is obviously done in .NET.
Also, If I replace the second chunk of code with
var models = MyRepository.GetActiveMyModel();
var filtered = from m in models
where m.Items.Count > 0
select new { m.Name };
It generates the initial SQL to retrieve the active records and then runs a separate SQL statement for each record to find out how many Items it has, rather than writing something like I'd expect:
SELECT Name
FROM MyModel m
WHERE Active = 1
AND (SELECT COUNT(*) FROM Items WHERE MyModelID = m.ID) > 0
You are returning IEnumerable<MyModel> from the method, which will cause in-memory evaluation from that point on, even if the underlying sequence is IQueryable<MyModel>.
If you want to allow code after GetActiveMyModel to add to the SQL query, return IQueryable<MyModel> instead.
You're running IEnumerable's extension method "Where" instead of IQueryable's. It will still evaluate lazily and give the same output, however it evaluates the IQueryable on entry and you're filtering the collection in memory instead of against the database.
When you later add an extra condition on another table (the count), it has to lazily fetch each and every one of the Items collections from the database since it has already evaluated the IQueryable before it knew about the condition.
(Yes, I would also like to be the extensive extension methods on IEnumerable to instead be virtual members, but, alas, they're not)

Is it possible to use existing classes as POCOs in Entity Framework

I was able to generate pocos using POCO template from Microsoft. It works great.
My question is how I could modify this or any other template to use existing objects from a different assembly, and just load data into them.
The way i tried going about it, was to create POCO's using the template, and the PocoGenerator.Context to use existing Model, and than modify generated code to return my clasess instead of its generated classes.
this gives me the dreaded "Mapping and metadata information could not be found for EntityType MyType".. This may be because there are a few extra fields in database that my object's don't have. I tried modifying entity objects and removing those fields but that cause some other problems..
has anyone done this?
UDPATE
yep, existing classes can be used. The one thing to watch out for, is the un-informative error above will be triggered if there is a mismatch between some property names, or types. Occasionally the runtime will give a meaningful error with name of property that is incompatible, but that's only if both classes are really close.
Anyways, to use existing classes as pocos, simply generate the pocos, and than comment out the generated classes. Than in xxxPocoGenerator.Context.cs add necessary namespace of your existing objects to be used.
As a side note, i wrote the following code to compare my existing classes, with POCO generated ones and show any that do not match so i could fix them.
var properties = typeof(MyExistingClass).GetProperties();
var tproperties = typeof(MyPOCOClass).GetProperties();
Console.WriteLine("---------------------------------Missing or Different Properties--------------------");
List<PropertyInfo> missingOrDifferentProperties = new List<PropertyInfo>();
foreach (var tp in tproperties)
if (properties.Where(p => p.Name == tp.Name && p.PropertyType == tp.PropertyType && p.CanRead == tp.CanRead && p.CanWrite == tp.CanWrite && p.IsSpecialName == tp.IsSpecialName && p.MemberType == tp.MemberType).Count() != 1)
Console.WriteLine(tp.Name + " :: " + tp.PropertyType.Name);
Sounds like you may be looking for Code-First which is in the latest CTP4 for Entity Framework.
See Link
as requested, here is my resolution as answer:
UDPATE
yep, existing classes can be used. The one thing to watch out for, is the un-informative error above will be triggered if there is a mismatch between some property names, or types. Occasionally the runtime will give a meaningful error with name of property that is incompatible, but that's only if both classes are really close.
Anyways, to use existing classes as pocos, simply generate the pocos, and than comment out the generated classes. Than in xxxPocoGenerator.Context.cs add necessary namespace of your existing objects to be used.
As a side note, i wrote the following code to compare my existing classes, with POCO generated ones and show any that do not match so i could fix them.
var properties = typeof(MyExistingClass).GetProperties();
var tproperties = typeof(MyPOCOClass).GetProperties();
Console.WriteLine("---------------------------------Missing or Different Properties--------------------");
List<PropertyInfo> missingOrDifferentProperties = new List<PropertyInfo>();
foreach (var tp in tproperties)
if (properties.Where(p => p.Name == tp.Name && p.PropertyType == tp.PropertyType && p.CanRead == tp.CanRead && p.CanWrite == tp.CanWrite && p.IsSpecialName == tp.IsSpecialName && p.MemberType == tp.MemberType).Count() != 1)
Console.WriteLine(tp.Name + " :: " + tp.PropertyType.Name);

LINQ to DataSet and xml help

I created a strongly-typed dataset in the dataset designer. The DataSet has a Table called FocusOffsetsTable and that table has four colums; SerialNumber, Filter, Wheel and Offset. I use the ReadXml() method of the DataSet class to load the strongly typed data from the xml file into the dataset. That seems to be working just fine.
I am trying to use a LINQ expression to try to get a Single row from this table but I can't seem to get the syntax correct. I want to use the Single() or SingleOrDefault() method to get just one row of data at a time but I am not sure how.
I have tried this FocusOffsets.FocusOffsetsTableRow x = FocusOffsetData.FocusOffsetsTable. but the Single() method is not available here. I also tried this...
FocusOffsets.FocusOffsetsTableRow x = (from offset in FocusOffsetData.FocusOffsetsTable
where offset.SerialNumber == mydevice.SerialNumber
where offset.Wheel == WheelID
where offset.Filter == FilterNum
select offset).Single();
but the Single method is not available here either.
I have done this before with tables in a SQL database before but this is my first time using a dataset from the dataset designer.
Have you added a using statement for System.Linq and included a reference to System.Data.DataSetExtensions. I think (but can't confirm since I'm on my Mac), that you ought to be able to do:
var x = FocusOffsetData.FocusOffsetsTable
.AsEnumerable()
.SingleOrDefault( o => o.SerialNumber == mydevice.SerialNumber
&& o.Wheel = WheelID
&& o.Filter = FilterNum );

Resources