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

时间:2014-06-16 14:45:48

标签: c# sql-server entity-framework performance

我正在尝试了解如何使用Entity Framework 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;
}

2 个答案:

答案 0 :(得分:2)

由于这些调用,您确实对数据库进行了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. As @Gert suggested,您应该使用外键而不是整个对象引用。
  2. 我没有考虑示例中null值的可能性,您可以将它们考虑在内using left joins with DefaultIfEmpty

答案 1 :(得分:1)

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

当然,首先应该设置外键值。在您的VehicleStateLogEntryDbo中,这可能如下所示:

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

ForeignKey属性告诉EF两个属性都属于外键关联。这也可以通过流畅的API配置,例如在OnModelCreating覆盖:

modelbuilder.Entry<VehicleStateLogEntryDbo>()
            .HasRequired(v => v.Vehicle)
            .WithMany()
            .HasForeignKey(v => v.VehicleIdentifier);

顺便说一下,只有Vehicle属性被称为独立关联

因此,当您拥有这些外键关联时,您只需设置FK值即可。也许您应该修改您的DTO以转移这些值而不是名称等。