简体   繁体   English

为什么 Entity Framework 在使用 Linq.Union() 或 .Except() 时会生成错误的列名?

[英]Why Entity Framework generates bad column names when using Linq .Union() or .Except()?

I'm using EF 6 in my project.我在我的项目中使用 EF 6。 All i want is to fetch some data from the database and sometimes join additional data.我想要的只是从数据库中获取一些数据,有时还加入其他数据。

Here is my code:这是我的代码:

var queryable = dbContext.Manuals
    .AsNoTracking()
    .Where(x => x.Status == "ACTIVE");

if (showDisabledParents)
{
    var parentsIds = queryable
        .Where(x => x.ParentId.HasValue)
        .Select(x => x.ParentId.Value)
        .Distinct()
        .ToArray();

    queryable = queryable.Union(
        dbContext.Manuals
            .AsNoTracking()
            .Where(x => parentsIds.Contains(x.Id))
    );
}

It works well when showDisabledParents is false .showDisabledParentsfalse时效果很好。 EF will generate a SQL query with correct column names. EF 将生成具有正确列名的 SQL 查询。

But when showDisabledParents is true then EF generates UNION statement(which is expected) but it uses C1, C2, C3... for column names.但是当showDisabledParents时,EF 会生成 UNION 语句(这是预期的),但它使用 C1、C2、C3... 作为列名。

And the problem is that i have a custom DbDataReader which calls DateTime.SpecifyKind(..., DateTimeKind.Utc) if column name ends with "Utc" .问题是我有一个自定义DbDataReader ,如果列名以"Utc"结尾,它会调用DateTime.SpecifyKind(..., DateTimeKind.Utc) And since EF is using wrong column names (C1, C2, C3, ...) my logic is not working.由于 EF 使用了错误的列名(C1、C2、C3 等),我的逻辑无法正常工作。

Is it somehow possible to prevent this behavior?是否有可能以某种方式阻止这种行为? I mean if there is a way to tell EF to not use these weird column names.我的意思是是否有办法告诉 EF 不要使用这些奇怪的列名。

UPDATED: Here is my DBDataReaderCode:更新:这是我的 DBDataReaderCode:

public class MyDbDataReader : DelegatingDbDataReader
{

private string _dbName;
public MyDbDataReader(string dbName, DbDataReader source)
    : base(source)
{
    _dbName = dbName;
}

public override DateTime GetDateTime(int ordinal)
{   
    return DateTime.SpecifyKind(base.GetDateTime(ordinal), base.GetName(ordinal).EndsWith("UTC", StringComparison.OrdinalIgnoreCase) 
        ? DateTimeKind.Utc 
        : DateTimeKind.Local);       
}

} }

I think this is not possible since EF uses internal aliases to produce the whole query and by that makes sure there are no duplicates in the names collected from different tables etc.我认为这是不可能的,因为 EF 使用内部别名来生成整个查询,并确保从不同表等收集的名称中没有重复项。

Anyhow, I would rather search for an issue about your approach to the dates.无论如何,我宁愿搜索有关您处理日期的问题。 Could you please provide some more details about your case?您能否提供有关您的案例的更多详细信息? In general the rule of thumb says to always persist your dates in UTC and convert to the specific time zone on demand.一般来说,经验法则是始终将您的日期保存在 UTC 中,并根据需要转换为特定时区。

The typical approach I use for handling DateTime.Kind is using an attribute on the applicable entity property.我用于处理 DateTime.Kind 的典型方法是在适用的实体属性上使用属性。 For instance this would go on any DateTime entity property that was UTC:例如,对于任何 UTC 日期时间实体属性,这将是 go:

    [DateTimeKind(DateTimeKind.Utc)]
    public DateTime SomeDateUTC { get; set; }

Where if you want other date times to be marked as Local time:如果您希望将其他日期时间标记为当地时间:

    [DateTimeKind(DateTimeKind.Local)]
    public DateTime SomeDate { get; set; }

DateTimes with no attribute would be left as Unspecified kind.没有属性的日期时间将保留为未指定类型。

The attribute itself: See ( Entity Framework DateTime and UTC )属性本身:参见( 实体框架日期时间和 UTC

Then in your DbContext, you just add this to the InitializeContext method:然后在您的 DbContext 中,只需将其添加到 InitializeContext 方法中:

((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized +=
    (sender, e) => DateTimeKindAttribute.Apply(e.Entity);

Otherwise, if you want to persist with your current method and you are dependent on the column name, Union cannot be counted on so split the condition into two queries.否则,如果你想坚持你当前的方法并且你依赖于列名,那么Union不能指望,所以将条件拆分为两个查询。 This will need to be done wherever the query is executed, so if your method is returning an IQueryable<Manual> , it would need to be changed to IEnumerable<IQueryable<Manual>> where if more than one query is returned the results are combined.这将需要在执行查询的任何地方完成,因此如果您的方法返回IQueryable<Manual> ,则需要将其更改为IEnumerable<IQueryable<Manual>> ,如果返回多个查询,则结果将合并.

var queryables = new List<IQueryable<Manual>();

queryables.Add(dbContext.Manuals
    .AsNoTracking()
    .Where(x => x.Status == "ACTIVE"));

if (showDisabledParents)
{
    var parentsIds = dbContext.Manuals
        .Where(x => x.Status == "ACTIVE"
            && x.ParentId.HasValue)
        .Select(x => x.ParentId.Value)
        .Distinct()
        .ToArray();

    queryables.Add = dbContext.Manuals
            .AsNoTracking()
            .Where(x => parentsIds.Contains(x.Id));
    
}

return queryables;

If you are instead executing the queries within this method then just capture the second query if it is needed and combine the results to be returned.如果您改为在此方法中执行查询,则只需捕获第二个查询(如果需要)并合并要返回的结果。

The trickiest bit here will be if you need to do pagination across the combined set.这里最棘手的一点是,如果您需要对组合集进行分页。 In this case I would instead use a two-pass approach, getting the relevant task IDs after appropriate sorting, then using a pagination fetch on the IDs, load the entities by ID:在这种情况下,我会改为使用两次通过的方法,在适当排序后获取相关的任务 ID,然后对 ID 使用分页提取,按 ID 加载实体:

var queryable = dbContext.Manuals
    .AsNoTracking()
    .Where(x => x.Status == "ACTIVE");

if (showDisabledParents)
{
    var parentsIds = queryable
        .Where(x => x.ParentId.HasValue)
        .Select(x => x.ParentId.Value)
        .Distinct()
        .ToArray();

    queryable = queryable.Union(
        dbContext.Manuals
            .AsNoTracking()
            .Where(x => parentsIds.Contains(x.Id))
    );
}

var ids = queryable
    .OrderBy(/* condition */)
    .Select(x => x.Id)
    .Skip(page * pageSize)
    .Take(pageSize)
    .ToList(); // Should execute union without worrying about column transformation since we have requested only IDs.

var manuals = await dbContext.Manuals.Where(x => ids.Contains(x.Id)).ToListAsync();

A few options to consider at least.至少要考虑几个选项。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM