简体   繁体   中英

Entity Framework Core Generating SQL With Ambiguous Column Names

I am using Entity Framework Core 2.2.6. I'm going to try and make this question concise and apologies in advance if it ends up being a wall of text. The error I am seeing is an ambiguous column name in the SQL Entity Framework Core generates.

So my situation is this: I have two entities with a many-to-one relationship. The "parent" entity implements an interface that has a property that is of type IChildEntity. Here are the interfaces:

public interface IParentEntity
{
    IChildEntity Child { get; set; }
    string Prop1 { get; set; }
    string Prop2 { get; set; }
}

public interface IChildEntity
{
    string ChildProp1 { get; set; }
    string ChildProp2 { get; set; } 
}

I am using ef core's fluent api and in order to set up the relationship between parent and child I am using a concrete type of ChildEntity and defining a IChildEntity property to conform to the interface and just passing things through to the concrete type:

public class ChildEntity : IChildEntity
{
    public long ID {get; set;}
    public string ChildProp1 { get; set; }
    public string ChildProp2 { get; set; }  
}

public class ParentEntity : IParentEntity
{
    public long ID { get; set; }
    public string Prop1 { get; set; }
    public string Prop2 { get; set; }
    public long ChildID { get; set; }
    // Navigation property so EF Core can create the relationship
    public ChildEntity MappedChild { get; private set; }

    // this is to adhere to the interface
    // just pass this through to the backing concrete instance
    [NotMapped]
    public IChildEntity Child
    {
        get => MappedChild;
        set => MappedChild = (ChildEntity)value;
    }   
}

Then in OnModelCreating I set up the relationship like so:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
   modelBuilder.Entity<ParentEntity>()
        .HasOne(e => e.MappedChild)
        .WithMany()
        .HasForeignKey(e => e.ChildID);
}

This works and the relationship gets set up as expected, however I am finding when I do a query it can generate some SQL that can result in an ambigous column error in some database engines. Here is the example query:

MyContext.ParentEntity
.Include(p => p.MappedChild)
.Where(p => p.Prop1.Equals("somestring")
.FirstOrDefault()

The SQL that gets generated is similar to:

SELECT p."ID", p."ChildID", p."Prop1", p."Prop1", "p.MappedChild"."ID", "p.MappedChild"."ChildProp1", "p.MappedChild"."ChildProp2"
FROM "ParentEntity" AS p
INNER JOIN "ChildEntity" AS "p.MappedChild" ON p."ChildID" = "p.MappedChild"."ID"
WHERE p."Prop1" = 'somestring'
ORDER BY "p.MappedChild"."ID"
LIMIT 1

The problem here is we are selecting two columns with the name ID and not aliasing. Some databases will be ok with this but some will not. A work around I can do for this is to do two separate queries to get the entity and the child entity:

var parent = MyContext.ParentEntity
            .Where(p => p.Prop1.Equals("somestring")
            .FirstOrDefault()
MyContext.Entry(parent).Reference(p => s.MappedChild).Load();

But this is less than ideal since it does multiple queries and is a bit less elegant than just using Include()

Because this seems like such a common use case and I couldn't find any bug reports against EF Core for this type of behavior it is my suspicion that I am doing something wrong here that is resulting in EFCore not aliasing column names for this type of query. I was thinking it could be the bit of trickery I have to do to ensure my entity implements it's interface (this is something I can't due to constraints in the codebase and other integrations) but the more I look at it the less likely that seems to me since we are directly dealing with the "mapped" property in EF related code and it's completely unaware of the interface.

My questions are - can anyone see something in my implementation that would cause this? Could anyone suggest a better workaround than what I have here? Any advice here would be appreciated. Thanks much.

This is an old Entity framework bug with the Oracle company products bug including the MySQL database and Oracle database (12.1 and older).

I see the

ORA-00918: column ambiguously defined

error mostly when:

  1. Selecting one entity with including parent entity.
  2. Selecting one entity with value object own one command

This error appears when using Find , First , FirstOrDefault , Last , Single and all single entity selector commands.

I tested many solutions and check generated sql statement to find out a very unique way without any performance overhead:

// This the way of getting one entity from oracle 12.1 without throwing Oracle exception => ORA-00918: column ambiguously defined without any extra overhead
var entities = await dbSet.Where(x => x.Id == id).Take(1).ToListAsync();
var entity = entities.FirstOrDefault();

Another Sample:

var entities = await dbSet.OrderByDescending(x => x.Id).Take(1).ToListAsync();
var entity = entities.FirstOrDefault();

At the end of your IQueryable Linq add Take(1) and get all with .ToList() or .ToListAsync() to execute the statement and fetch a list with one record. Then use Enumerable Single Entity Selector to change the list to an entity.

That's all.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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