Reputation: 21979
As noted in "Loading Related Data" from EF Core Documentation we can use .Include(..)
to Eagerly Load navigation properties from the DbSet
(or generic IQueryable<T>
linking back to an EF context).
This means that, given the following models:
public class TestEntityA
{
public int Id { get; set; }
public int TestEntityBId { get; set; }
public TestEntityB TestEntityB { get; set; }
public string BProperty { get { return TestEntityB.Property; } }
}
public class TestEntityB
{
public int Id { get; set; }
public string Property { get; set; }
}
.. then code such as the following should work:
context.TestEntityAs
.Include(m => m.TestEntityB)
.Any(m => m.BProperty == "Hello World");
/*
* Note that the below DOES work by using the nav property directly
* in the query, but that is not always going to be an option for
* whatever reason. If it's .Included it should be available through
* INNER JOINing it into the base query before the .Any runs.
* .Any(m => m.TestEntityB.Property == "Hello World");
*/
However it doesn't.
I note that there is a caveat where .Include()
could be ignored should a query not return the type that is initially requested:
If you change the query so that it no longer returns instances of the entity type that the query began with, then the include operators are ignored. [snip] By default, EF Core will log a warning when include operators are ignored.
I'm not sure how, in the above call to .Any()
that is relevant. Yes, the query is not returning the original type (it's returning a bool
of course) but at the same time, the Warning is also not logged to advise that it is being ignored.
My questions here are:
.ToList()
) but that would obviously load everything, to find out if we have anything on a .Any()
which could easily be a query (and would be as such in EF6). What is a workaround to get this .Any()
to work on the server side thus not requiring the ToList to put it in memory?Workaround:
context.TestEntityAs
.Include(m => m.TestEntityB)
.ToList()
.Any(m => m.BProperty == "Hello World");
Full reproducible sample: https://gist.github.com/rudiv/3aa3e1bb65b86ec78ec6f5620ee236ab
Upvotes: 9
Views: 6714
Reputation: 131
by naming convention, it should be worked you can try using this piece of code to manually configure relations between your models
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<TestEntityA>()
.HasOne(x => x.TestEntityB)
.WithMany()
.HasForeignKey(x => x.TestEntityBId);
}
Upvotes: 1
Reputation: 3513
I am not sure if this answers your question but it is kind of a workaround.
change BProperty
to be a method like getBProperty
public string getBProperty (TestEntityB tb) { return tb.Property; }
and then write your query like
context.TestEntityAs
//.Include(m => m.TestEntityB) --- you don't need include anymore!
.Any(m => m.getBProperty(m.TestEntityB)) == "Hello World");
Upvotes: 0
Reputation: 168
I'm not sure but try [ForeignKey(nameof(TestEntityBId ))]
public class TestEntityA
{
public int Id { get; set; }
public int TestEntityBId { get; set; }
[ForeignKey(nameof(TestEntityBId ))] public TestEntityB TestEntityB { get; set; }
public string BProperty { get { return TestEntityB.Property; } }
}
public class TestEntityB
{
public int Id { get; set; }
public string Property { get; set; }
}
Upvotes: 0
Reputation: 21
Depending on the data, likely the efficient way to achieve that would be to load a single record:
context.TestEntityAs
.Include(m => m.TestEntityB)
.Where(m => m.BProperty == "Hello World")
.FirstOrDefault() != null;
Upvotes: 0
Reputation: 773
The behaviour is expected but you can use explicit loading for more efficient query as shown below.
2 separate queries, but no need to load all of TestEntityBs
// First query
var testEntityAs = context.TestEntityAs.ToList();
var testEntityAsIds = testEntityAs.Select(t => t.Id);
// Second query, can apply filter of Hello World without loading all TestEntityBs
context.TestEntityBs
.Where(t => testEntityAsIds.Contains(t.Id) && t.Property == "Hello World")
.Load();
// In memory check
var isAny = testEntityAs.Any(t => !string.IsNullOrEmpty(t.BProperty));
Upvotes: 0