【问题标题】:What is the cause of this EF6 behaviour Find() does not return a proxy but Single() does return a proxy这种 EF6 行为的原因是什么 Find() 不返回代理但 Single() 确实返回代理
【发布时间】:2015-04-25 07:17:50
【问题描述】:

我有一个 POCO 类 BankAccount,即 public,所有成员都是 public properties,导航属性设置为 virtual

Entity Framework 6.1.2 使用Find() 方法正确地将其从数据库中加载为 POCO。但是,据我所知,它应该返回一个 Proxy 类实例而不是 POCO 实例!事实上,当我使用Single()SingleOrDefault()First()FirstOrDefault() 时,会正确返回一个代理类实例。

发生了什么,这是预期的行为,如果不是,是什么导致了这种情况发生?

这里是 POCO 类:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.Serialization;

namespace AbcBankModels
{
    //[DataContract]
    [Table("BankAccount")]
    public class BankAccount
    {
        [Key]
        [Column(Order = 1)]
        //[DataMember]
        //[Range(0, 9999999)]
        //[StringLength(7, MinimumLength = 7)]
        //[Display(Name = "Account Number")]
        public virtual string BankAccountId { get; set; }

        [Key]
        [Column(Order = 2)]
        //[DataMember]
        //[Range(0, 999999)]
        //[StringLength(6, MinimumLength = 6)]
        //[Display(Name = "Sort Code")]
        public virtual string BankBranchId { get; set; }

        //[DataMember]
        public virtual BankBranch BankBranch { get; set; }

        //[DataMember]
        //[ForeignKey("ApplicationUser")]
        public virtual string ApplicationUserId { get; set; }

        //[DataMember]
        public virtual User ApplicationUser { get; set; }

        //[DataMember]
        public virtual ICollection<BankCard> BankCardList { get; set; }

        //[DataMember]
        public virtual ICollection<BankTransaction> BankTransactionList { get; set; }

        //[DataMember]
        //[Display(Name = "Account Status")]
        //[EnumDataType(typeof(EnumAccountStatus))]
        public virtual EnumAccountStatus AccountStatus { get; set; }

        //[DataMember]
        //[Display(Name = "Account Type")]
        //[EnumDataType(typeof(EnumBankAccountType))]
        public virtual EnumBankAccountType AccountType { get; set; }

        //[DataMember]
        //[DataType(DataType.DateTime)]
        //[Display(Name = "Date Account Opened")]
        public virtual DateTime? AccountCreationDateTime { get; set; }

        //[DataMember]
        //[DataType(DataType.DateTime)]
        //[Display(Name = "Date Account Closed")]
        public virtual DateTime? AccountClosureDateTime { get; set; }

        //[DataMember]
        //[DataType(DataType.Currency)]
        //[Display(Name = "Account Overdraft Limit")]
        public virtual decimal AccountOverdraft { get; set; }

        //[DataMember]
        //[Display(Name = "Account Overdraft Interest Rate")]
        public virtual decimal AccountOverdraftInterestRate { get; set; }

        //[DataMember]
        //[Display(Name = "Account Overdraft Usage Monthly Fee")]
        public virtual decimal AccountOverdraftFacilityMonthlyCost { get; set; }

        //[DataMember]
        //[Display(Name = "Account Monthly Fee")]
        public virtual decimal AccountMonthlyCost { get; set; }

        //[DataMember]
        //[Display(Name = "Account Interest Rate")]
        public virtual decimal AccountInterestRate { get; set; }

    }
}

这是返回代理的方法:

    public static BankAccount FindBankAccount(ApplicationDbContext applicationDbContext, string bankAccountId, string bankBranchId, string userId)
    {
        if (String.IsNullOrWhiteSpace(bankAccountId) || String.IsNullOrWhiteSpace(bankBranchId)) return null;

        var bankAccount = applicationDbContext.BankAccountList.SingleOrDefault(a => a.BankAccountId == bankAccountId && a.BankBranchId == bankBranchId);

        if (bankAccount == null) return null;

        if (string.IsNullOrWhiteSpace(userId)) return bankAccount;

        if (bankAccount.ApplicationUserId != userId) return null;

        return bankAccount;
    }

这是不返回代理的方法:

    public static BankAccount FindBankAccount(ApplicationDbContext applicationDbContext, string bankAccountId,
        string bankBranchId, string userId)
    {
        if (String.IsNullOrWhiteSpace(bankAccountId) || String.IsNullOrWhiteSpace(bankBranchId)) return null;

        var bankAccount = applicationDbContext.BankAccountList.Find(bankAccountId, bankBranchId);

        if (bankAccount == null) return null;

        if (string.IsNullOrWhiteSpace(userId)) return bankAccount;

        if (bankAccount.ApplicationUserId != userId) return null;

        return bankAccount;
    }

【问题讨论】:

    标签: c# entity-framework entity poco proxy-classes


    【解决方案1】:

    如果您的上下文在您查询时已经使用该键跟踪非代理 BankAccount,则可能会发生这种情况。

    奇怪的是,虽然 First 和 Single 总是查询数据库,但它们应该返回与 Find 相同的实体。

    例如,如果您有一个运行此代码的单元测试:

            Foo nonProxy = new Foo { Id = 4, Name = "Foo 4" }; // values correspond to an existing entity in db
            ApplicationDbContext ctx = new ApplicationDbContext();          
            ctx.Foos.Attach(nonProxy);
            Assert.AreSame(nonProxy, ctx.Foos.Find(nonProxy.Id));
            Assert.AreSame(nonProxy, ctx.Foos.First(c => c.Name == "Foo 4"));
            Assert.AreSame(nonProxy, ctx.Foos.FirstOrDefault(c => c.Name == "Foo 4"));
            Assert.AreSame(nonProxy, ctx.Foos.Single(c => c.Name == "Foo 4"));
            Assert.AreSame(nonProxy, ctx.Foos.SingleOrDefault(c => c.Name == "Foo 4"));
    
            ctx = new ApplicationDbContext();
            Foo proxy = ctx.Foos.Find(nonProxy.Id);
            Assert.AreSame(proxy, ctx.Foos.Find(nonProxy.Id));
            Assert.AreSame(proxy, ctx.Foos.First(c => c.Name == "Foo 4"));
            Assert.AreSame(proxy, ctx.Foos.FirstOrDefault(c => c.Name == "Foo 4"));
            Assert.AreSame(proxy, ctx.Foos.Single(c => c.Name == "Foo 4"));
            Assert.AreSame(proxy, ctx.Foos.SingleOrDefault(c => c.Name == "Foo 4"));
    

    那么它应该可以正常运行。在上下文跟踪具有相同键的实体后​​,它们都返回对同一对象的引用。

    参考Querying/Finding Entities

    // Query for the Blog named ADO.NET Blog 
    var blog = context.Blogs.Where(b => b.Name == "ADO.NET Blog").FirstOrDefault();
    

    当从数据库返回结果时,不存在的对象 在上下文中附加到上下文。如果一个对象已经在 上下文,返回现有对象(当前和原始 条目中对象属性的值不会被覆盖 与数据库值)。

    因此,您可能在以下情况下使用 Find

    也许也有用:DbSet.Find Method

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-13
      • 1970-01-01
      • 2021-03-22
      • 1970-01-01
      • 1970-01-01
      • 2021-04-22
      相关资源
      最近更新 更多