Test if an entity has a reference without loading it in FK-less models (solution code provided)
- Dominant language
- C#
- Stars
- 14.8k
- Forks
- 3.4k
- PR merge metrics
- PR metrics pending
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
{
///
/// 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.
///
///
///
///
///
/// Request a load if exists
///
///
///
public static bool HasReference(
this EntityEntry entry,
Expression> 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()
```
///
/// 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.
///
///
///
///
/// Request a load if exists
///
///
public static bool Exist(
this ReferenceEntry 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:
Contributor guide
Assessment
This issue has not been assessed yet.