这个EF6代码应该有查询吗?

本文关键字:查询 EF6 代码 这个 | 更新日期: 2023-09-27 18:08:45

我试图了解如何使用实体框架6。下面的代码可以工作。但是,对于一个写操作,它似乎有四个查询。五次分别访问数据库似乎是不对的。我想要一个数据库调用,根据需要向每个表添加适当的项。有没有更好的方法来做下面的代码?或者它真的在SaveChanges调用中执行单个数据库命中吗?

public bool Write(ILogEntry logEntry)
{
    var log = logEntry as AssetStateLogEntry;
    if (log == null) return false;
    using (var db = _dbContextProvider.ConstructContext())
    {
        if (db != null)
        {
            var state = new VehicleStateLogEntryDbo
            {
                LogSource = db.LogSources.FirstOrDefault(l => l.Name == log.Source.ToString())
                    ?? new LogSourceDbo {Name = log.Source.ToString()}, 
                Message = log.Message, 
                TimeStamp = log.TimeStamp.ToUniversalTime(), 
                Vehicle = db.Vehicles.FirstOrDefault(v => v.Name == log.Asset.Name) 
                    ?? new VehicleDbo {Name = log.Asset.Name, VehicleIdentifier = log.Asset.ID}, 
                VehicleState = db.VehicleStates.FirstOrDefault(v => v.Name == log.StateValue.ToString() && v.VehicleStateType.Name == log.StateType.ToString())
                    ?? new VehicleStateDbo
                    {
                        Name = log.StateValue.ToString(),
                        VehicleStateType = db.VehicleStateCategories.FirstOrDefault(c => c.Name == log.StateType.ToString()) 
                            ?? new VehicleStateTypeDbo {Name = log.StateType.ToString()},
                    }
            };
            db.VehicleStateLogEntrys.Add(state);
            db.SaveChanges();
        }
    }
    return true;
}

这个EF6代码应该有查询吗?

作为这些调用的结果,您确实对数据库进行了4次查询:

  • db.LogSources.FirstOrDefault
  • db.Vehicles.FirstOrDefault
  • db.VehicleStates.FirstOrDefault
  • db.VehicleStateCategories.FirstOrDefault

当您调用FirstOrDefault时,LINQ查询被执行,因此,数据库被击中。

我不知道你的模式,但也许你可以将它们中的一些连接到一个LINQ查询中(至少Vehicles*表似乎是相关的)。


编辑:使用OP

请求的连接的示例查询

把下面的查询作为我建议的起点,你还没有提供你的实体,所以这只是给你一个想法:

from l in db.LogSources
join v in db.Vehicles on l.Asset.ID equals v.VehicleIdentifier 
join vs in db.VehicleStates on vs.VehicleIdentifier equals v.VehicleIdentifier
where l.Name == log.Source.ToString() 
   && v.Name == log.Asset.Name
   && vs.Name == log.StateValue.ToString() 
   && vs.VehicleStateType.Name == log.StateType.ToString()
select new VehicleStateLogEntryDbo
{
     LogSource = l,
     Message = log.Message,
     TimeStamp = log.TimeStamp.ToUniversalTime(),
     Vehicle = s,
     VehicleState = vs
}

注意事项:

  1. 正如@Gert建议的那样,你应该使用外键而不是整个对象引用。
  2. 我没有考虑null值在示例中的可能性,您可以使用左连接与DefaultIfEmpty来考虑它们。

不应该设置对象引用,而应该设置原始外键值。从面向对象的角度来看,这听起来像是异端邪说,但当涉及到有效地设置关联时,它是实体框架推荐的方法。

当然,首先应该设置外键值。在VehicleStateLogEntryDbo中,这可能看起来像:

public int VehicleIdentifier { get; set; } // or guid?
[ForeignKey("VehicleIdentifier")]
public VehicleDbo Vehicle { get; set }

ForeignKey属性告诉EF两个属性属于一个外键关联。这也可以通过fluent API来配置,例如在OnModelCreating override中:

modelbuilder.Entry<VehicleStateLogEntryDbo>()
            .HasRequired(v => v.Vehicle)
            .WithMany()
            .HasForeignKey(v => v.VehicleIdentifier);
顺便说一下,只有Vehicle属性被称为独立于的关联

因此,当您有这些外键关联时,您可以简单地设置FK值。也许你应该修改你的DTO来传输这些值,而不是名称等。