dotnet / dotnet/efcore

Test if an entity has a reference without loading it in FK-less models (solution code provided)

Open
#36,935 1 comment 0 reactions 0 assignees View on GitHub
area-change-tracking customer-reported
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:

Image

### 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:

Image

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.