-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Open
Open
Test if an entity has a reference without loading it in FK-less models (solution code provided)#36935
Feature
Copy link
Milestone
Description
What problem are you trying to solve?
Get if an entity has a reference without loading it:
- I want to keep my model classes without foreign key values to avoid ambiguity (that works great!)
- I can't overload the database
I want to to like this:
if (!Db.Entry(order).HasReference(x => x.Customer))
...
Instead of this:
Db.Entry(order).Reference(x => x.Customer).Load();
if (order.Customer == null)
...
Describe the solution you'd like
I'm currently using this solution it can be a standard feature?
using System;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Metadata;
namespace T4.Data.Lib
{
public static class EntityEntryExtensions
{
/// <summary>
/// Checks whether the reference navigation has an FK value. It also load the reference if requested with loadIfExist param
/// Returns true if the FK is set (even if already loaded), otherwise false.
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TProperty"></typeparam>
/// <param name="entry"></param>
/// <param name="navigationExpression"></param>
/// <param name="loadIfExist">Request a load if exists</param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="InvalidOperationException"></exception>
public static bool HasReference<TEntity, TProperty>(
this EntityEntry<TEntity> entry,
Expression<Func<TEntity, TProperty>> navigationExpression, Boolean loadIfExist = false)
where TEntity : class
{
// Get property name from the lambda expression
if (navigationExpression.Body is not MemberExpression member)
throw new ArgumentException("Expression must be a simple member access", nameof(navigationExpression));
var navName = member.Member.Name;
// Get the navigation metadata
var navigationEntry = entry.Navigations.FirstOrDefault(n => n.Metadata.Name == navName);
if (navigationEntry == null)
throw new InvalidOperationException($"Navigation '{navName}' not found on entity '{typeof(TEntity).Name}'.");
// Get metadata for navigation
var navigationMetadata = navigationEntry.Metadata as INavigation;
if (navigationMetadata == null)
throw new InvalidOperationException($"Navigation '{navName}' is not a reference navigation.");
// Get FK property metadata (composite keys supported)
var fkProps = navigationMetadata.ForeignKey.Properties;
// Check if all FK properties have values
bool exists = fkProps.All(p => entry.Property(p.Name).CurrentValue != null);
// Load if exists
if (exists && loadIfExist)
{
// Load if not already loaded
var navEntry = entry.Reference(navName);
if (!navEntry.IsLoaded)
navEntry.Load();
}
return exists;
}
}
}
Usage example:

Or better Exist() inside Reference()
/// <summary>
/// Checks whether the reference navigation has an FK value.
/// It can also load the reference if requested with loadIfExist.
/// Returns true if the FK is set (even if already loaded), otherwise false.
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TProperty"></typeparam>
/// <param name="reference"></param>
/// <param name="loadIfExist">Request a load if exists</param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public static bool Exist<TEntity, TProperty>(
this ReferenceEntry<TEntity, TProperty> reference,
bool loadIfExist = false)
where TEntity : class
where TProperty : class
{
var entry = reference.EntityEntry;
var navigationMetadata = reference.Metadata as INavigation
?? throw new InvalidOperationException($"Navigation '{reference.Metadata.Name}' is not a reference navigation.");
// Get FK property metadata (composite keys supported)
var fkProps = navigationMetadata.ForeignKey.Properties;
// Check if all FK properties have values
bool exists = fkProps.All(p => entry.Property(p.Name).CurrentValue != null);
// Load if exists and not loaded
if (exists && loadIfExist && !reference.IsLoaded)
reference.Load();
return exists;
}
Usage example:
