Skip to content

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

@e-500

Description

@e-500

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:

Image

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:

Image

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions