我正在使用EF core 2.1.14这是我通过脚手架创建的DbContext类:
public partial class AgriDbContext : DbContext
{
public AgriDbContext()
{
}
public AgriDbContext(string connectionString)
: base(GetOptions(connectionString))
{
}
private static DbContextOptions GetOptions(string connectionString)
{
return SqlServerDbContextOptionsExtensions.UseSqlServer(new DbContextOptionsBuilder(), connectionString).Options;
}
public virtual DbSet<Advertisement> Advertisements { get; set; }
public virtual DbSet<AgroItem> AgroItems { get; set; }
public virtual DbSet<BuyerAddsDifferentAdsToFav> BuyerAddsDifferentAdsToFavs { get; set; }
public virtual DbSet<BuyersAddAgroItemToInterest> BuyersAddAgroItemToInterests { get; set; }
public virtual DbSet<Category> Categories { get; set; }
public virtual DbSet<City> Cities { get; set; }
public virtual DbSet<SellersFavoritesBuyer> SellersFavoritesBuyers { get; set; }
public virtual DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasAnnotation("ProductVersion", "2.2.0-rtm-35687");
modelBuilder.Entity<Advertisement>(entity =>
{
entity.Property(e => e.Picture).IsUnicode(false);
entity.HasOne(d => d.City)
.WithMany(p => p.Advertisements)
.HasForeignKey(d => d.CityId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("ADVERTISEMENTS_INCLUDE_A_CITY");
entity.HasOne(d => d.Item)
.WithMany(p => p.Advertisements)
.HasForeignKey(d => d.ItemId)
.HasConstraintName("AN_ADVERTISEMENT_IS_RELATED_TO_AN_ITEM");
entity.HasOne(d => d.Seller)
.WithMany(p => p.Advertisements)
.HasForeignKey(d => d.SellerId)
.HasConstraintName("USERS_POST_ADVERTISEMENTS");
});
modelBuilder.Entity<AgroItem>(entity =>
{
entity.Property(e => e.Name).IsUnicode(false);
entity.Property(e => e.Uname).IsUnicode(false);
entity.HasOne(d => d.Category)
.WithMany(p => p.AgroItems)
.HasForeignKey(d => d.CategoryId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("AGRO_ITEMS_BELONG_TO_A_CATEGORY");
});
modelBuilder.Entity<BuyerAddsDifferentAdsToFav>(entity =>
{
entity.HasKey(e => new { e.BuyerId, e.AdId });
entity.HasOne(d => d.Ad)
.WithMany(p => p.BuyerAddsDifferentAdsToFavs)
.HasForeignKey(d => d.AdId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_BUYER_ADDS_DIFFERENT_ADS_TO_FAV_ADVERTISEMENTS");
entity.HasOne(d => d.Buyer)
.WithMany(p => p.BuyerAddsDifferentAdsToFavs)
.HasForeignKey(d => d.BuyerId)
.HasConstraintName("FK_BUYER_ADDS_DIFFERENT_ADS_TO_FAV_USERS");
});
modelBuilder.Entity<BuyersAddAgroItemToInterest>(entity =>
{
entity.HasKey(e => new { e.BuyerId, e.ItemId });
entity.HasOne(d => d.Buyer)
.WithMany(p => p.BuyersAddAgroItemToInterests)
.HasForeignKey(d => d.BuyerId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_BUYERS_ADD_AGRO_ITEM_TO_INTEREST_USERS");
entity.HasOne(d => d.Item)
.WithMany(p => p.BuyersAddAgroItemToInterests)
.HasForeignKey(d => d.ItemId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_BUYERS_ADD_AGRO_ITEM_TO_INTEREST_AGRO_ITEMS");
});
modelBuilder.Entity<Category>(entity =>
{
entity.Property(e => e.Name).IsUnicode(false);
entity.Property(e => e.Uname).IsUnicode(false);
});
modelBuilder.Entity<City>(entity =>
{
entity.HasIndex(e => e.Id)
.HasName("UNIQUE_LOCATION")
.IsUnique();
entity.Property(e => e.Name).IsUnicode(false);
});
modelBuilder.Entity<SellersFavoritesBuyer>(entity =>
{
entity.HasKey(e => new { e.SellerId, e.BuyerId });
entity.HasOne(d => d.Buyer)
.WithMany(p => p.SellersFavoritesBuyerBuyers)
.HasForeignKey(d => d.BuyerId)
.HasConstraintName("FK_SELLERS_FAVORITES_BUYERS_USERS1");
entity.HasOne(d => d.Seller)
.WithMany(p => p.SellersFavoritesBuyerSellers)
.HasForeignKey(d => d.SellerId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_SELLERS_FAVORITES_BUYERS_USERS");
});
modelBuilder.Entity<User>(entity =>
{
entity.HasIndex(e => new { e.CcompanyCode, e.CcountryCode, e.Cphone })
.HasName("UNIQUE_CONTACT")
.IsUnique();
entity.Property(e => e.Address).IsUnicode(false);
entity.Property(e => e.Fname).IsUnicode(false);
entity.Property(e => e.Lname).IsUnicode(false);
entity.HasOne(d => d.City)
.WithMany(p => p.Users)
.HasForeignKey(d => d.CityId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_USERS_CITIES");
});
OnModelCreatingPartial(modelBuilder);
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}
在上面的代码中,CityId与外键正确相关,并且在User类中还有City的属性。但是,每当我有一个用户时,它都会在City属性中返回null,但会从数据库中返回CityId。
下面是我的实体模型类
[Table("USERS")]
public partial class User
{
public User()
{
Advertisements = new HashSet<Advertisement>();
BuyerAddsDifferentAdsToFavs = new HashSet<BuyerAddsDifferentAdsToFav>();
BuyersAddAgroItemToInterests = new HashSet<BuyersAddAgroItemToInterest>();
SellersFavoritesBuyerBuyers = new HashSet<SellersFavoritesBuyer>();
SellersFavoritesBuyerSellers = new HashSet<SellersFavoritesBuyer>();
}
public long Id { get; set; }
[Required]
[Column("FName")]
public string Fname { get; set; }
[Required]
[Column("LName")]
public string Lname { get; set; }
[Required]
[Column("CCompanyCode")]
[StringLength(3)]
public string CcompanyCode { get; set; }
[Required]
[Column("CCountryCode")]
[StringLength(3)]
public string CcountryCode { get; set; }
[Required]
[Column("CPhone")]
[StringLength(7)]
public string Cphone { get; set; }
[Required]
public string Address { get; set; }
[Column("GLat", TypeName = "decimal(10, 8)")]
public decimal? Glat { get; set; }
[Column("GLng", TypeName = "decimal(11, 8)")]
public decimal? Glng { get; set; }
public bool BuyerFlag { get; set; }
public bool SellerFlag { get; set; }
public short CityId { get; set; }
[ForeignKey("CityId")]
[InverseProperty("Users")]
public virtual City City { get; set; }
[InverseProperty("Seller")]
public virtual ICollection<Advertisement> Advertisements { get; set; }
[InverseProperty("Buyer")]
public virtual ICollection<BuyerAddsDifferentAdsToFav> BuyerAddsDifferentAdsToFavs { get; set; }
[InverseProperty("Buyer")]
public virtual ICollection<BuyersAddAgroItemToInterest> BuyersAddAgroItemToInterests { get; set; }
[InverseProperty("Buyer")]
public virtual ICollection<SellersFavoritesBuyer> SellersFavoritesBuyerBuyers { get; set; }
[InverseProperty("Seller")]
public virtual ICollection<SellersFavoritesBuyer> SellersFavoritesBuyerSellers { get; set; }
}
我正在使用这种存储库方法来获取用户
public EFarmer.Models.User GetUser(ContactNumberFormat contact)
{
var user = users
.Where(x => x.CcountryCode == contact.CountryCode
&& x.CcompanyCode == contact.CompanyCode
&& x.Cphone == contact.PhoneNumber).First() ?? null;
return (user != null) ? new EFarmer.Models.User
{
Address = user.Address,
City = EFarmer.Models.City.Convert(user.City),
ContactNumber = new ContactNumberFormat(user.CcountryCode, user.CcompanyCode, user.Cphone),
IsBuyer = user.BuyerFlag,
IsSeller = user.SellerFlag,
Location = new GeoLocation { Latitude = user.Glat, Longitude = user.Glng },
Name = new NameFormat { FirstName = user.Fname, LastName = user.Lname },
Id = user.Id
} : null;
}
这是将实体模型转换为我的业务模型的转换方法,由于实体模型中City中的空值,它给出了空引用异常
public static City Convert(EFarmerPkModelLibrary.Entities.City city)
{
return new City
{
GeoLocation = new GeoLocation { Latitude = city.Glat, Longitude = city.Glng },
Id = city.Id,
Name = city.Name
};
}
从您的示例和标签(EF Core 2.1)中可以看到,问题可能是尚未启用延迟加载。检查是否已添加EF Core Proxies的依赖项,并将optionsBuilder.UseLazyLoadingProxies();
添加到DbContext OnModelConfiguring
替代中。
不过,建议不要滚动自己的映射器,例如静态Convert方法,而要利用现有的映射器,例如AutoMapper。与EF集成的Automapper的关键功能是投影(ProjectTo
)。这可以将您的DTO / ViewModels构建为馈入数据库的Linq表达式的一部分,从而导致查询效率大大提高,并且不会出现诸如延迟加载之类的多次点击。
[使用延迟加载时,您将有1个查询命中来从用户中获取所有字段,然后将有1个查询命中来获取您所在城市的所有字段,再加上1个命中,则每个其他延迟加载调用都将得到。如果您正在执行获取用户列表之类的操作,则将获得1次点击才能获得用户列表,但是每个城市的每个用户都会获得1次点击!
急切加载,您将有1个查询命中,以从您的用户及其相关城市中获取所有字段,这是性能较低的命中。但是,无论您的DTO / ViewModel是否需要它们,它仍在获取all字段。还存在忘记显式渴望查询中与负载相关的数据的风险,尤其是在扩展实体以添加新关系时,这最终会导致延迟加载性能损失或问题。
使用Automapper的ProjectTo
,您将有1次查询命中,仅从User,City和DTO / ViewModel请求的任何其他相关实体中获取字段。无需记住与Include
亲戚在一起的时间,也不必担心懒惰的打击。干净,高效且面向未来。
这是有关Entity Framework如何管理延迟加载的全部内容。此答案List returned by Entity Framework is null
中已经解决了相似的主题