这种EF6行为的原因是什么Find()不返回代理,但Single()确实返回代理

时间:2015-02-23 20:22:36

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

我有一个POCO课程BankAccountpublic,所有成员都是public properties,导航属性设置为virtual

实体框架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;
    }

1 个答案:

答案 0 :(得分:2)

如果您的上下文在查询时已经跟踪了具有该密钥的非代理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,或者您在使用它之前在代码中的某个位置禁用Configuration.ProxyCreationEnabled。

也许有用:DbSet.Find Method