【问题标题】:Unable to access Entity Framework navigation properties无法访问实体框架导航属性
【发布时间】:2023-03-18 00:20:01
【问题描述】:

我有一个捕获 AssetRequest 应用程序的类 AssetRequest。对于每个 AssetRequest 应用程序,用户可以选择 1 个或多个附件对象附加到该应用程序。我无法访问附加到 AssetRequest 实例的附件。这就是我对数据建模的方式(数据库优先):

资产请求

public partial class AssetRequest
{
public AssetRequest()
        {
            this.AssetRequestAccessories = new HashSet<AssetRequestAccessories>();
        }
public int AssetRequestId { get; set; }
....
....
        public Nullable<int> DepartmentId { get; set; }
 public virtual Accessories Accessories { get; set; }   
}

与每个 AssetRequest 关联的附件被保存到不同的表中,并带有指向 AssetRequestId 的引用外键:

资产请求配件

public partial class AssetRequestAccessories
{
    public int AssetRequestAccessoryId { get; set; }
    public Nullable<int> AssetRequestId { get; set; }
    public Nullable<int> AccessoryId { get; set; }
    public string CreatedBy { get; set; }
    public virtual AssetRequest AssetRequest { get; set; }
    public virtual Accessories Accessories { get; set; }

}

要获取可能已附加到 AssetRequest 应用程序的任何附件的实际名称,我已经定义了 AccesoryId(在 AssetRequestAccessories 中):

public partial class Accessories
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public Accessories()
    {
        this.AssetRequest = new HashSet<AssetRequest>();
        this.AssetRequestAccessories = new HashSet<AssetRequestAccessories>();
    }

    public int AccesoryId { get; set; }
    public string AccessoryName { get; set; }
    public string CreatedBy { get; set; }
    public Nullable<System.DateTime> CreatedOn { get; set; }          
    public virtual ICollection<AssetRequest> AssetRequest { get; set; }       
    public virtual ICollection<AssetRequestAccessories> AssetRequestAccessories { get; set; }
}

在 AssetRequest 表中仅维护 AssetRequestId,然后 AssetRequestAccessories 将维护附加到应用程序的所有附件,如下所示:

我似乎找不到从给定 AssetRequest 条目直接访问 Accessories 对象的方法。尝试使用导航属性,但它正确地指向了 未设置为实例的对象引用

    public ActionResult Index()
    {           
        var services = db.AssetRequest.Include(d => d.Location).Include(d => d.AssetRequestAccessories).Include(d => d.Accessories).Include(d => d.Department).Include(d => d.RequestType).Include(d => d.EmployeeStatus).Where(d => d.IsClosed==1);
        return View(services.ToList());

    }

这就是我想要做的(不工作);

     @foreach (var item in Model)
        {
            <h5>  <span class="label label-danger arrowed">@item.RequestReference</span></h5>          
 <table id="simple-table" class="table  table-bordered table-hover">
                                                        <thead>
                                                            <tr>                                                                <th>Accessory name</th>                                                              
                                                            </tr>
                                                        </thead>
                                                        <tbody>
                                                            @if (Model.Count() <= 0)
                                                            {
                                                                <tr> <td colspan="3"> No Accessories found for this Request </td> </tr>
                                                            }
                                                            else
                                                            {                                                          
          foreach (var item in Model)
                                                                    {
                                                                    <tr>                                                                    <td>@Html.DisplayFor(modelItem => item.AssetRequestAccessories.Accessories.AccessoryName) </td>
    }
     </tbody>
    </table>
    }

我尝试使用带有连接的匿名类型,然后我将其转换为 AssetRequest,但仍不确定正确的实现:

 public AssetRequest GetAssetRequest()
    {         
        var assetreqquest = (from j in appEntities.AssetRequest
                             join f in appEntities.AssetRequestAccessories
                             on j.AssetRequestId equals f.AssetRequestId
                             join d in appEntities.Accessories
                             on f.AccessoryId equals d.AccesoryId
                             select new AssetRequest
                         {
                             d.AccessoryName,
                             j.RequestReference,
                             ...
                             ...
                         });
    }

我错过了什么?

【问题讨论】:

  • 旁注,类不应以复数形式命名; EF 可以处理您调用您的配件附件并拥有List&lt;Accessory&gt; Accessories
  • 这是什么风格(核心/非)和版本的 EF? “不工作”是什么意思?
  • 那么,AssertRequest:Accessory 之间的这个many:many 是由AssetRequestAccessories 表分解成一对one:many 的吗?如果是这样,我希望您的查询看起来更像context.AssetRequest.Include(ar =&gt; ar.AssetRequestAccessories).ThenInclude(ara =&gt; ara.Accessories),如果它是 EF 核心(请注意 THEN INCLUDE),并且如果它是 EF6,则对集合的访问更复杂
  • @CaiusJard 我正在使用 EF6。我不确定 many:many ,但是我的建模是 one:many 用于 AssetRequests to Accessories。我可以在 EF6 中使用什么
  • 如果关系 AR:A 是 1:M 我不明白为什么你有一个 ARA 实体(具有一对单数 ID 属性,链接到单个 AR 和单个 A 即通常这是将 M:M 分解为两个 1:M) 的中间人表的模式。为什么你的 AR 实体不只是有 A 的集合?

标签: c# entity-framework foreign-keys navigation-properties


【解决方案1】:

根据我从您的数据模型中看到的情况及其可能的预期用途,您希望 AssetRequests 和 Accessories 之间存在多对多关系。因此 AssetRequestAccessories 链接表。这不是一个 链接表,因为您还希望跟踪 CreatedOn/CreatedBy 等详细信息,因此 EF6 无法在没有连接实体的情况下将其屏蔽为多对多。 EF 支持多对多的方式有两种,一种是没有加入实体,另一种是有加入实体。您经常会看到可互换使用“多对多”的示例,但这可能有点误导。两种关系都使用一个连接表,但只有一种关系将该连接表映射到一个实体。

从 EF 实体看,纯多对多将如下所示:

public class AssetRequest
{
    //  ...
    public virtual ICollection<Accessory> Accessories { get; set; } = new HashSet<Accessory>();
}

public class Accessory
{
    // ...
    public virtual ICollection<AssetRequest> AssetRequests { get; set; } = new HashSet<AssetRequest>();
}

这是沿线映射的:

modelBuilder.Entity<AssetRequest>()
    .HasMany(x => x.Accessories)
    .WithMany(x => x.AssetRequests>();

您可以选择在此处提供有关链接表的详细信息,包括名称和各自的左右外键名称。

此选项仅在加入表(AssetRequestAccessories)是纯链接表时有效,这意味着它仅包含 AssetRequestId 和 AccessoryId 的复合键。 (/w FK 对其各自表的引用)没有其他列。它 使用起来更简单,因为我们的实体有效地相互引用。 EF 完全在幕后制定链接表。

如果您想为链接表提供其他列,事情会变得有点复杂。您实际上形成了更多的一对多对一关系,而不是多对多关系。在这种情况下,您的实体和映射需要稍作更改:

public class AssetRequest
{
    //  ...
    public virtual ICollection<AssetRequestAccessory> AssetRequestAccessories { get; set; } = new HashSet<AssetRequestAccessory>();
}

public class Accessory
{
    // ...
    public virtual ICollection<AssetRequestAccessory> AssetRequestAccessories { get; set; } = new HashSet<AssetRequestAccessory>();
}

public class AssetRequestAccessory
{
    [Key]
    public int AssetRequestAccessoryId { get; set; }
    public int CreatedBy { get; set; }
    public DateTime CreatedOn { get; set; }

    public virtual AssetRequest AssetRequest { get; set; }
    public virtual Accessory Accessory { get; set; }
}

当涉及到映射关系时:

modelBuilder.Entity<AssetRequest>()
    .HasMany(x => x.AssetRequestAccessories)
    .WithRequired(x => x.AssetRequest);
modelBuilder.Entity<Accessory>()
    .HasMany(x => x.AssetRequestAccessories>()
    .WithRequired(x => x.Accessory);

通常在示例中,您会看到这些集合的名称如下:

ICollection<AssetRequestAccessory> Accessories

ICollection<AssetRequestAccessory> AssetRequests

在各自的类中,但我建议不要这样做,因为命名意味着这些集合包含它们不包含的东西。它通常会导致新开发人员的困惑。 :)

这意味着当您想要获取 AssetRequest 的附件时,您必须浏览链接实体。

在纯模型中你可以去哪里:

foreach (var accessory in assetRequest.Accessories)

...对于链接实体,您需要稍微调整一下:

foreach (var accessory in assetRequest.AssetRequestAccessories.Select(x => x.Accessory))

当然有点笨拙,但它确实允许您支持 CreatedOn/By 以及适用于资产请求和附件组合的任何其他详细信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-10
    • 2016-03-29
    • 2016-05-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多