StepUp
StepUp

Reputation: 38199

Mapping from join - The type 'CarDTO' appears in two structurally incompatible initializations within a single LINQ to Entities query

I am trying to map into CarDTO from right side of left join, but gets the following error:

The type 'CarDTO' appears in two structurally incompatible initializations within a single LINQ to Entities query. A type can be initialized in two places in the same query, but only if the same properties are set in both places and those properties are set in the same order.

I am trying to make self left join and my query looks like this:

var query = from left in db.Cars
    join right in db.Cars on left.id_Base equals right.ID into rightGrouped
    from rightGr in rightGrouped.DefaultIfEmpty()
    select new CarDTO()
    {
        ID = left.ID,
        Description = left.DisplayName,
        Name = left.Name,
        IsSystem = left != null ? left.Template.isSystem : (byte?)null,
        IdBase = left.id_Base,                            
        InnerCar = new CarDTO()
        {
            ID = rightGr.ID,
            Description = rightGr.DisplayName,
            Name = rightGr.Name,
            IsSystem = rightGr != null ? rightGr.Template.isSystem : (byte?)null, 
            IdBase = rightGr.id_Base,
            InnerCar = new CarDTO()                                
        }                            
    };

If I change the following row InnerCar = new CarDTO() to InnerCar = null, then I get the following error:

Unable to create a null constant value of type 'CarDTO'. Only entity types, enumeration types or primitive types are supported in this context.`

Does anybody know what I am doing wrong?

Upvotes: 2

Views: 1755

Answers (1)

Evk
Evk

Reputation: 101593

Error message explains that EF expects all initializations of the same type (CarDto in this case) in the same expression to follow a rule: in each initialization the same properties should be set, and in the same order. Your code violates this rule, because you have 3 initializations. 2 of them set the same properties in same order, but third one (last InnerCar = new CarDTO()) does not.

The only possible value to use in InnerCar.InnerCar initialization to satisfy this rule is null (because of recursive definition). However, EF will not allow you to do that, because it cannot create constant values of arbitrary types (doesn't know how to convert such thing into SQL).

So doing this with recursive type is just not possible. Instead, we should use different type for InnerCar. This can be done for example with anonymous types:

select new
{
    ID = left.ID,
    Description = left.DisplayName,
    Name = left.Name,
    IsSystem = left != null ? left.Template.isSystem : (byte?)null,
    IdBase = left.id_Base,                            
    InnerCar = new
    {
        ID = rightGr.ID,
        Description = rightGr.DisplayName,
        Name = rightGr.Name,
        IsSystem = rightGr != null ? rightGr.Template.isSystem : (byte?)null, 
        IdBase = rightGr.id_Base                               
    }                            
};

This will work, because two anonymous types here are different. First one has 6 properties (including InnerCar) and second one has 5 properties (without InnerCar). Of course for type to be anonymous is not a requirement here.

You can also just flatten this structure:

select new
{
    LeftID = left.ID,
    RightID = rightGr.ID,
    // etc
};

After you performed such mapping, and have done any other filtering\paging\whatever - convert it to your type after materializing:

// query here is complete
var result = query.AsEnumerable().Select(c => {
    new CarDto {
        ID = c.ID,
        Description = c.Description,
        // etc
    }
});

Upvotes: 1

Related Questions