我有一个POCO课程BankAccount
,public
,所有成员都是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;
}
答案 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"));
然后它应该运行没有错误。在上下文跟踪具有相同键的实体之后,它们都返回对同一对象的引用。
// 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