2

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.

7
  • 1
    LIMIT 1 is only used in MySQL. The query is created by the database provider, not EF Core itself. Which provider are you using? Most likely it has a bug, although this query is nothing unusual. Commented Aug 2, 2019 at 11:45
  • Hi @PanagiotisKanavos LIMIT 1 is not only used in MySQL, Postgres uses it too :-). The provider I am actually having an issue with is Oracle db 12.1 - I'm using their beta ODP.NET provider. I tried the same query with Oracle provider and Postgres and got mostly the same SQL which lead me to believe some of it was generated by EFCore itself. Are you saying none of the SQL is generated from EFCore? I dug through the source a bit but couldn't find anything definitive Commented Aug 2, 2019 at 12:09
  • I'd expect a query preceding this query which pulls the parent data from the database. The query you show should be a second query to satisfy the Include. With SQL Server this second query only selects child columns and I think that's the bug in the MySql query provider: it shouldn't query parent columns. Commented Aug 2, 2019 at 12:12
  • 1
    Update the question specifying the precise provider versions in the question itself. The SQL queries are generated by the provider, not EF Core itself. EF Core doesn't know what syntax each database supports. The standard way to return 1 row is to use FETCH FIRST, not LIMIT, and yet, the query uses LIMIT. Commented Aug 2, 2019 at 12:18
  • 1
    @TheMethod have you tried to use a different provider, like Devart's? Their providers are far leaner and faster than Oracle's. They've supported EF Core for quite some time now Commented Aug 2, 2019 at 12:41

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.