导航属性无法加载 - EF

时间:2016-04-09 16:46:11

标签: c# sql-server entity-framework

我有3个模型Items,Branches和ItemsInBranches定义如下

public class Items
{
    public int Id { get; set; }
    public string Barcode { get; set; }
    public string Name { get; set; }
    public int SizeId { get; set; }
    public int Price { get; set; }
    public int DiscountId { get; set; }
    public int ShortageMargin { get; set; }
    [NotMapped]
    public double ActualPrice
    {
        get
        {
            double amount = ((double)Price * (double)Discount.Amount / 100);
            double price = (Price - amount < 0) ? 0 : Price - amount;
            return price;
        }
    }

    public Discounts Discount { get; set; }
    public ItemSizes Size { get; set; }
    public ICollection<ItemsInBranches> ItemsInBrach { get; set; }
}

public class Branches
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Location { get; set; }

    public ICollection<Employees> Employees { get; set; }
    public ICollection<TransactionLog> TransacionLogs { get; set; }
    public ICollection<ItemsInBranches> ItemsInBranch { get; set; }
}

public class ItemsInBranches
{
    public int Id { get; set; }
    public int ItemId { get; set; }
    public int BranchId { get; set; }
    public int Amount { get; set; }

    [NotMapped]
    public bool IsShort
    {
        get
        {
            return Amount < Item.ShortageMargin;
        }
    }

    public Items Item { get; set; }
    public Branches Branch { get; set; }
}

每当我尝试使用以下代码在分支中加载项目时,我的分支导航属性加载得很好但是我的Items总是设置为null

    public IEnumerable<StorageViewModel> GetStorage(int? BranchId)
    {
        var storage = Find(x => true).Select(s => new StorageViewModel
        {
            Amount = s.Amount,
            BranchName = s.Branch.Name,
            ItemName = s.Item.Name,
            SortageMargin = s.Item.ShortageMargin,
            IsShort = s.IsShort
        });
        return storage;
    }

    public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
    {
        return _dataContext.Set<TEntity>().Where(predicate);
    }

我确保将ItemId和BranchId设置为数据库中Items和Branches表的外键,它们不允许空值并强制执行外键约束 任何人都可以告诉我为什么只有分支被加载而Item总是设置为null

1 个答案:

答案 0 :(得分:2)

一般情况下,你不应该尝试包装框架,特别是EF和你使用帮助方法的EF一起使用Find方法,我提到这是因为这就是你遇到的原因这个问题(相反,它使得解决方案比没有帮助者时更难解决)。

当您发现AsNoTracking()及其工作原理时,您可能会在Find ChangeTracker中遇到此问题(或者您希望在{1}}指定SaveChanges()的问题{ {1}}开始运行缓慢或由于一次连接过多而导致内存占用空间大。当然,这对EF来说更具问题,因为它使得人们在没有真正理解交易等重要概念的情况下开发太容易了 - 它试图抽象出来的层的细节......)

您需要以某种方式加载相关实体。 I'd recommend reading this link and choosing an option.此链接上的一个选项仅触及您的GetStorage方法,可能会轻松解决问题&#34;但是如果有很多记录可能不是很好的表现 - 考虑到你的谓词包括所有内容,但可能没有太多。如果您没有看到更多代码,我无法为您提供更好的建议。如果它加载一个而不是另一个并且它们相同(相同的非可空FK并且那里有相应的记录)那么我认为它可能在某处...阅读链接以了解如何配置导航属性的加载。请注意,这可能会生成多个SELECT语句;由于在Set<>内部使用Find来获取您需要的缺失记录,我无法想出更好的方法(除了深入GetStorage)。如果您可以从指定了不同Find的其他类调用TEntity,那么您可能只能在一个SELECT语句中获得所需的Items条记录 - 但我不会了解您如何设置服务及其生命周期。这可能是性能之间的折衷,也没有数据访问包装器的大规模重构。

var allEntities = Find(x => true).ToList();
allEntities.ForEach(x => _dataContext.Entry(x).Reference(y => y. Item).Load());
var storage = allEntities.Select(s => new StorageViewModel
// ...

GetStorage似乎特定于TEntity;您的Find方法似乎已将TEntity定义为包含类中的泛型 - 所以如果我不得不猜测这些方法在(2)不同的类中,即使您将它们背靠背放置。那么你的Find方法可能是在一个抽象EF的类上给你一个更简单的&#34;数据库的接口。 EF已经是数据库的简单接口;你不需要另一个。

您可以做的是制作具体的存储库,这些存储库严格依赖于特定的TEntity并依赖于DbContext,然后让您的域逻辑依赖于您的存储库(这将要求您以某种方式模拟存储库 - 使用伪数据库,实际数据库或泄漏的内存数据库到&#34;单元&#34;测试您的域逻辑)或让您的存储库完全解耦从您的域逻辑,启用快速执行域逻辑单元测试。然后你的存储库已经从EF中屏蔽了应用程序的其余部分,因此你可以将其更改为ADO.NET或更轻量级的ORM,如Dapper。

这样的Find方法是一个EF抽象 - 即使你没有意识到,你也可以依赖于EF(以及你的模式)。我简要描述的是数据库抽象 - 如果需要,它实际上可以让您自由地更改数据访问(和模式) - 只需更改存储库实现而不更改其API或行为......