1

我正在使用 EF Core 3.1.7。DbContext 设置了 UseLazyLoadingProxies。Fluent API 映射用于将实体映射到数据库。我有一个带有导航属性的实体,该属性使用支持字段。加载和保存到数据库似乎工作正常,除了在我访问导航属性之前访问支持字段时出现问题。

似乎引用的实体在访问支持字段时不会延迟加载。这是 Castle.Proxy 类的缺陷还是配置不正确?

比较 IsRegisteredForACourse 的 Student 类实现与 IsRegisteredForACourse2 的相关行为。

数据库表和关系。 学生和课程

学生实体

using System.Collections.Generic;

namespace EFCoreMappingTests
{
    public class Student
    {
        public int Id { get; }
        public string Name { get; }

        private readonly List<Course> _courses;
        public virtual IReadOnlyList<Course> Courses => _courses.AsReadOnly();

        protected Student()
        {
            _courses = new List<Course>();
        }

        public Student(string name) : this()
        {
            Name = name;
        }

        public bool IsRegisteredForACourse()
        {
            return _courses.Count > 0;
        }

        public bool IsRegisteredForACourse2()
        {
            //Note the use of the property compare to the previous method using the backing field.
            return Courses.Count > 0;
        }

        public void AddCourse(Course course)
        {
            _courses.Add(course);
        }
    }
}

课程实体

namespace EFCoreMappingTests
{
    public class Course
    {
        public int Id { get; }
        public string Name { get; }
        public virtual Student Student { get; }

        protected Course()
        {
        }
        public Course(string name) : this()
        {
            Name = name;
        }
    }
}

数据库上下文

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace EFCoreMappingTests
{
    public sealed class Context : DbContext
    {
        private readonly string _connectionString;
        private readonly bool _useConsoleLogger;

        public DbSet<Student> Students { get; set; }
        public DbSet<Course> Courses { get; set; }

        public Context(string connectionString, bool useConsoleLogger)
        {
            _connectionString = connectionString;
            _useConsoleLogger = useConsoleLogger;
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
            {
                builder
                    .AddFilter((category, level) =>
                        category == DbLoggerCategory.Database.Command.Name && level == LogLevel.Information)
                    .AddConsole();
            });

            optionsBuilder
                .UseSqlServer(_connectionString)
                .UseLazyLoadingProxies(); 

            if (_useConsoleLogger)
            {
                optionsBuilder
                    .UseLoggerFactory(loggerFactory)
                    .EnableSensitiveDataLogging();
            }
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Student>(x =>
            {
                x.ToTable("Student").HasKey(k => k.Id);
                x.Property(p => p.Id).HasColumnName("Id");
                x.Property(p => p.Name).HasColumnName("Name");
                x.HasMany(p => p.Courses)
                    .WithOne(p => p.Student)
                    .OnDelete(DeleteBehavior.Cascade)
                    .Metadata.PrincipalToDependent.SetPropertyAccessMode(PropertyAccessMode.Field);
            });
            modelBuilder.Entity<Course>(x =>
            {
                x.ToTable("Course").HasKey(k => k.Id);
                x.Property(p => p.Id).HasColumnName("Id");
                x.Property(p => p.Name).HasColumnName("Name");
                x.HasOne(p => p.Student).WithMany(p => p.Courses);
                
            });
        }
    }
}

演示问题的测试程序。

using Microsoft.Extensions.Configuration;
using System;
using System.IO;
using System.Linq;

namespace EFCoreMappingTests
{
    class Program
    {
        static void Main(string[] args)
        {
            string connectionString = GetConnectionString();

            using var context = new Context(connectionString, true);

            var student2 = context.Students.FirstOrDefault(q => q.Id == 5);

            Console.WriteLine(student2.IsRegisteredForACourse());
            Console.WriteLine(student2.IsRegisteredForACourse2()); // The method uses the property which forces the lazy loading of the entities
            Console.WriteLine(student2.IsRegisteredForACourse());
        }

        private static string GetConnectionString()
        {
            IConfigurationRoot configuration = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json")
                .Build();

            return configuration["ConnectionString"];
        }
    }
}

控制台输出

False
True
True
4

1 回答 1

0

当您将 EF 实体中的映射属性声明为虚拟时,EF 会生成一个代理,该代理能够拦截请求并评估是否需要加载数据。如果您尝试在访问该虚拟属性之前使用支持字段,则 EF 没有“信号”来延迟加载该属性。

作为实体的一般规则,您应该始终使用属性并避免使用/访问支持字段。自动初始化可以帮助:

public virtual IReadOnlyList<Course> Courses => new List<Course>().AsReadOnly();
于 2020-08-13T04:59:18.890 回答