Building expression trees to filter derived entity classes

F

Fritz

Hi,

How can a access members of defered entity classes using an expression tree?
The problem: as soon a filtered entity is of another derived entity class.

Having derived entity classes like:

public abstract class Profile {
public ProfileType ProfileType;
}

public class ProfileCircular : Profile {
public float Diameter;
}

public class ProfileRectangle : Profile {
public float SizeX;
public float SizeY;
}

The goal is to dynamically building expression trees to filter objects of
this classes.
All works fine if I filter for Profile.ProfileType, which is a member of the
entity base class. But how can I filter mixed objects of types
ProfileCircular and ProfileRectangle?
For example the filter should extract all ProfileCircular objects with
Diameter >= 40.0.
The expression tree build to do so looks like:

Lambda - Expression`1
AndAlso - BinaryExpression
Conditional - ConditionalExpression
TypeIs - TypeBinaryExpression
Parameter - ParameterExpression
GreaterThanOrEqual - BinaryExpression
MemberAccess - MemberExpression
TypeAs - UnaryExpression
Parameter - ParameterExpression
Constant - ConstantExpression
Constant - ConstantExpression

At execution time of query an InvalidOperationException is raised saying:
"Member access Diameter' of 'ProfileCircular' not legal on type
'ProfileRectangle."

I expected that member access to Diameter would not be executed on objects
of type ProfileRectangle due to the ConditionalException testing for "TypeIs
ProfileCircular".

Thanks for any help, Fritz
 
P

Pavel Minaev

Hi,

How can a access members of defered entity classes using an expression tree?
The problem: as soon a filtered entity is of another derived entity class..

Having derived entity classes like:

public abstract class Profile {
   public ProfileType ProfileType;

}

public class ProfileCircular : Profile {
   public float Diameter;

}

public class ProfileRectangle : Profile {
   public float SizeX;
   public float SizeY;

}

The goal is to dynamically building expression trees to filter objects of
this classes.
All works fine if I filter for Profile.ProfileType, which is a member of the
entity base class. But how can I filter mixed objects of types
ProfileCircular and ProfileRectangle?
For example the filter should extract all ProfileCircular objects with
Diameter >= 40.0.
The expression tree build to do so looks like:

Lambda  -  Expression`1
   AndAlso  -  BinaryExpression
      Conditional  -  ConditionalExpression
         TypeIs  -  TypeBinaryExpression
            Parameter  -  ParameterExpression
         GreaterThanOrEqual  -  BinaryExpression
            MemberAccess  -  MemberExpression
               TypeAs  -  UnaryExpression
                  Parameter  -  ParameterExpression
            Constant  -  ConstantExpression
         Constant  -  ConstantExpression

At execution time of query an InvalidOperationException is raised saying:
"Member access Diameter' of 'ProfileCircular' not legal on type
'ProfileRectangle."

I expected that member access to Diameter would not be executed on objects
of type ProfileRectangle due to the ConditionalException testing for "TypeIs
ProfileCircular".

Please clarify what flavor of LINQ you're using (to objects, SQL, EF
etc). Also, can you include the value returned by ToString() for the
expression tree that you've built (it's hard to read it the way it's
spelt out in your post, and it omits actual values in the nodes)?
 
F

Fritz

Pavel Minaev said:
Please clarify what flavor of LINQ you're using (to objects, SQL, EF
etc). Also, can you include the value returned by ToString() for the
expression tree that you've built (it's hard to read it the way it's
spelt out in your post, and it omits actual values in the nodes)?

Hi Pavel

The expression tree in literal is:

Lambda - e => IIF((e Is ProfileCircular), (Convert((e As
ProfileCircular).Diameter) >= 40), True)
Conditional - IIF((e Is ProfileCircular), (Convert((e As
ProfileCircular).Diameter) >= 40), True)
TypeIs - (e Is ProfileCircular)
Parameter - e
GreaterThanOrEqual - (Convert((e As ProfileCircular).Diameter) >= 40)
Convert - Convert((e As ProfileCircular).Diameter)
MemberAccess - (e As ProfileCircular).Diameter
TypeAs - (e As ProfileCircular)
Parameter - e
Constant - 40
Constant - True


Flavor is LINQ to SQL. Here is the code where the expression tree is applieed:

Expression<Func<Profile, bool>> filterExpression =
filter.GetExpression<Profile>();

var dal = DalFactory.Instance.CreateDal<Profile, IQueryable<Profile>,
Int32>();
var result =
dal.ReadAll()
.Where(filterExpression)
.Select(e => e);

Thanks for investigation, Fritz
 
P

Pavel Minaev

Hi Pavel

The expression tree in literal is:

Lambda  -  e => IIF((e Is ProfileCircular), (Convert((e As
ProfileCircular).Diameter) >= 40), True)
   Conditional  -  IIF((e Is ProfileCircular), (Convert((e As
ProfileCircular).Diameter) >= 40), True)
      TypeIs  -  (e Is ProfileCircular)
         Parameter  -  e
      GreaterThanOrEqual  -  (Convert((e As ProfileCircular).Diameter) >= 40)
         Convert  -  Convert((e As ProfileCircular).Diameter)
            MemberAccess  -  (e As ProfileCircular).Diameter
               TypeAs  -  (e As ProfileCircular)
                  Parameter  -  e
         Constant  -  40
      Constant  -  True

Flavor is LINQ to SQL. Here is the code where the expression tree is applieed:

Expression<Func<Profile, bool>> filterExpression =
filter.GetExpression<Profile>();

var dal = DalFactory.Instance.CreateDal<Profile, IQueryable<Profile>,
Int32>();
var result =
    dal.ReadAll()
    .Where(filterExpression)
    .Select(e => e);

I strongly suspect that this is a LINQ to SQL limitation. Having
written my own query-translation ORM in the past, I know that handling
"is/as" (or analogs) correctly with semantics identical to the host
language can be quite a challenge in SQL, so I'm not surprised there.
Interestingly, LINQ to SQL docs claim that, while C-style casts aren't
supported by the translator in projections, operators "is" and "as"
are, with no other comments. I've made a simple console app with SQL
server database, and tested inheritance in a query identical to yours
("o is Derived1 ? (o as Derived1).Member == 123 : true"), and it
worked fine for me, even with objects of all possible types in the
table.

I'm not so sure where to go from here now... are you sure you have set
up the inheritance correctly in your LINQ to SQL mapping? Aside from
that, I can't really think of anything. Maybe if you provide your
table definition for the hierarchy, and the mapping, it would be
possible to repro that and see what's wrong.
 
F

Fritz

Pavel Minaev said:
I'm not so sure where to go from here now... are you sure you have set
up the inheritance correctly in your LINQ to SQL mapping? Aside from
that, I can't really think of anything. Maybe if you provide your
table definition for the hierarchy, and the mapping, it would be
possible to repro that and see what's wrong.

Here the code for translating SQL data to domain classes:

IQueryable<DomainModel.Profile> result =
from p in DC.Profiles
select
p.ProfileTyp == (ushort)DomainModel.ProfileType.Rund ?
new DomainModel.ProfileCircular
{
// if next statement is missing
// exception "has no supported translation to SQL"
// would be raised when using ProfileTyp as parameter
// in expression tree for dynamic filtering
ProfileTyp = (DomainModel.ProfileType)p.ProfileTyp,
ID = p.ID,
Name = p.Name,
Diameter = (p as CircularProfile).Diameter,
Comment = p.Comment,
modified = p.modified,
Version = p.Version
} :
p.ProfileTyp == (ushort)DomainModel.ProfileType.Rechteck ?
new DomainModel.ProfileRectangle
{
ProfileTyp = (DomainModel.ProfileType)p.ProfileTyp,
ID = p.ID,
Name = p.Name,
Size_X = (p as RectangleProfile).Size_X,
Size_Y = (p as RectangleProfile).Size_Y,
EdgeRadius = (p as RectangleProfile).EdgeRadius,
Comment = p.Comment,
modified = p.modified,
Version = p.Version
} :
p.ProfileTyp == (ushort)DomainModel.ProfileType.Sonder ?
new DomainModel.ProfileArbitrary
{
ProfileTyp = (DomainModel.ProfileType)p.ProfileTyp,
ID = p.ID,
Name = p.Name,
Size_X = (p as ArbitraryProfile).Size_X,
Size_Y = (p as ArbitraryProfile).Size_Y,
CenterOfArea_X = (p as ArbitraryProfile).CenterOfArea_X,
CenterOfArea_Y = (p as ArbitraryProfile).CenterOfArea_Y,
Drawing = (p as ArbitraryProfile).Drawing,
Comment = p.Comment,
modified = p.modified,
Version = p.Version
} :
new DomainModel.Profile();
return result;





Here the mapping definition:

[Table(Name = "Profiles")]
[InheritanceMapping(Code = "0", Type = typeof(CircularProfile), IsDefault =
true)]
[InheritanceMapping(Code = "1", Type = typeof(RectangleProfile))]
[InheritanceMapping(Code = "10", Type = typeof(ArbitraryProfile))]
public partial class Profile : INotifyPropertyChanging, INotifyPropertyChanged
{

private static PropertyChangingEventArgs emptyChangingEventArgs = new
PropertyChangingEventArgs(String.Empty);

private int _ProfileID;

private ushort _ProfileTyp;

private string _Name;

private string _Comment;

private System.Nullable<System.DateTime> _modified;

private System.Data.Linq.Binary _Stamp;

private EntitySet<Pipe> _Pipes;

private EntitySet<BendTool> _Tools;

#region Extensibility Method Definitions
partial void OnLoaded();
partial void OnValidate(System.Data.Linq.ChangeAction action);
partial void OnCreated();
partial void OnIDChanging(int value);
partial void OnIDChanged();
partial void OnProfileTypChanging(ushort value);
partial void OnProfileTypChanged();
partial void OnNameChanging(string value);
partial void OnNameChanged();
partial void OnCommentChanging(string value);
partial void OnCommentChanged();
partial void OnmodifiedChanging(System.Nullable<System.DateTime> value);
partial void OnmodifiedChanged();
partial void OnVersionChanging(System.Data.Linq.Binary value);
partial void OnVersionChanged();
#endregion

public Profile()
{
this._Pipes = new EntitySet<Pipe>(new Action<Pipe>(this.attach_Pipes),
new Action<Pipe>(this.detach_Pipes));
this._Tools = new EntitySet<BendTool>(new
Action<BendTool>(this.attach_Tools), new Action<BendTool>(this.detach_Tools));
OnCreated();
}

[Column(Storage = "_ProfileID", AutoSync = AutoSync.OnInsert, DbType =
"Int NOT NULL IDENTITY", IsPrimaryKey = true, IsDbGenerated = true)]
public int ID
{
get
{
return this._ProfileID;
}
set
{
if ((this._ProfileID != value))
{
this.OnIDChanging(value);
this.SendPropertyChanging();
this._ProfileID = value;
this.SendPropertyChanged("ID");
this.OnIDChanged();
}
}
}

[Column(Storage = "_ProfileTyp", IsDiscriminator = true)]
public ushort ProfileTyp
{
get
{
return this._ProfileTyp;
}
set
{
if ((this._ProfileTyp != value))
{
this.OnProfileTypChanging(value);
this.SendPropertyChanging();
this._ProfileTyp = value;
this.SendPropertyChanged("ProfileTyp");
this.OnProfileTypChanged();
}
}
}

[Column(Storage = "_Name", DbType = "NVarChar(50)", CanBeNull = false)]
public string Name
{
get
{
return this._Name;
}
set
{
if ((this._Name != value))
{
this.OnNameChanging(value);
this.SendPropertyChanging();
this._Name = value;
this.SendPropertyChanged("Name");
this.OnNameChanged();
}
}
}

[Column(Storage = "_Comment", DbType = "NVarChar(300)")]
public string Comment
{
get
{
return this._Comment;
}
set
{
if ((this._Comment != value))
{
this.OnCommentChanging(value);
this.SendPropertyChanging();
this._Comment = value;
this.SendPropertyChanged("Comment");
this.OnCommentChanged();
}
}
}

[Column(Storage = "_modified", DbType = "DateTime", UpdateCheck =
UpdateCheck.Never)]
public System.Nullable<System.DateTime> modified
{
get
{
return this._modified;
}
set
{
if ((this._modified != value))
{
this.OnmodifiedChanging(value);
this.SendPropertyChanging();
this._modified = value;
this.SendPropertyChanged("modified");
this.OnmodifiedChanged();
}
}
}

[Column(Storage = "_Stamp", AutoSync = AutoSync.Always, DbType =
"TIMESTAMP", CanBeNull = false, IsDbGenerated = true, IsVersion = true)]
public System.Data.Linq.Binary Version
{
get
{
return this._Stamp;
}
set
{
if ((this._Stamp != value))
{
this.OnVersionChanging(value);
this.SendPropertyChanging();
this._Stamp = value;
this.SendPropertyChanged("Version");
this.OnVersionChanged();
}
}
}

[Association(Name = "Profile_Pipe", Storage = "_Pipes", ThisKey = "ID",
OtherKey = "ProfileID")]
public EntitySet<Pipe> Pipes
{
get
{
return this._Pipes;
}
set
{
this._Pipes.Assign(value);
}
}

[Association(Name = "Profile_BendTool", Storage = "_Tools", ThisKey =
"ID", OtherKey = "ProfileID")]
public EntitySet<BendTool> Tools
{
get
{
return this._Tools;
}
set
{
this._Tools.Assign(value);
}
}

public event PropertyChangingEventHandler PropertyChanging;

public event PropertyChangedEventHandler PropertyChanged;

protected virtual void SendPropertyChanging()
{
if ((this.PropertyChanging != null))
{
this.PropertyChanging(this, emptyChangingEventArgs);
}
}

protected virtual void SendPropertyChanged(String propertyName)
{
if ((this.PropertyChanged != null))
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}

private void attach_Pipes(Pipe entity)
{
this.SendPropertyChanging();
entity.Profile = this;
}

private void detach_Pipes(Pipe entity)
{
this.SendPropertyChanging();
entity.Profile = null;
}

private void attach_Tools(BendTool entity)
{
this.SendPropertyChanging();
entity.Profile = this;
}

private void detach_Tools(BendTool entity)
{
this.SendPropertyChanging();
entity.Profile = null;
}
}

public partial class CircularProfile : Profile
{

private System.Nullable<float> _Diameter;

#region Extensibility Method Definitions
partial void OnLoaded();
partial void OnValidate(System.Data.Linq.ChangeAction action);
partial void OnCreated();
partial void OnDiameterChanging(System.Nullable<float> value);
partial void OnDiameterChanged();
#endregion

public CircularProfile()
{
OnCreated();
}

[Column(Storage = "_Diameter")]
public System.Nullable<float> Diameter
{
get
{
return this._Diameter;
}
set
{
if ((this._Diameter != value))
{
this.OnDiameterChanging(value);
this.SendPropertyChanging();
this._Diameter = value;
this.SendPropertyChanged("Diameter");
this.OnDiameterChanged();
}
}
}
}

public partial class NonCircularProfile : Profile
{

private System.Nullable<float> _Size_X;

private System.Nullable<float> _Size_Y;

#region Extensibility Method Definitions
partial void OnLoaded();
partial void OnValidate(System.Data.Linq.ChangeAction action);
partial void OnCreated();
partial void OnSize_XChanging(System.Nullable<float> value);
partial void OnSize_XChanged();
partial void OnSize_YChanging(System.Nullable<float> value);
partial void OnSize_YChanged();
#endregion

public NonCircularProfile()
{
OnCreated();
}

[Column(Storage = "_Size_X")]
public System.Nullable<float> Size_X
{
get
{
return this._Size_X;
}
set
{
if ((this._Size_X != value))
{
this.OnSize_XChanging(value);
this.SendPropertyChanging();
this._Size_X = value;
this.SendPropertyChanged("Size_X");
this.OnSize_XChanged();
}
}
}

[Column(Storage = "_Size_Y")]
public System.Nullable<float> Size_Y
{
get
{
return this._Size_Y;
}
set
{
if ((this._Size_Y != value))
{
this.OnSize_YChanging(value);
this.SendPropertyChanging();
this._Size_Y = value;
this.SendPropertyChanged("Size_Y");
this.OnSize_YChanged();
}
}
}
}

public partial class RectangleProfile : NonCircularProfile
{

private System.Nullable<float> _EdgeRadius;

#region Extensibility Method Definitions
partial void OnLoaded();
partial void OnValidate(System.Data.Linq.ChangeAction action);
partial void OnCreated();
partial void OnEdgeRadiusChanging(System.Nullable<float> value);
partial void OnEdgeRadiusChanged();
#endregion

public RectangleProfile()
{
OnCreated();
}

[Column(Storage = "_EdgeRadius")]
public System.Nullable<float> EdgeRadius
{
get
{
return this._EdgeRadius;
}
set
{
if ((this._EdgeRadius != value))
{
this.OnEdgeRadiusChanging(value);
this.SendPropertyChanging();
this._EdgeRadius = value;
this.SendPropertyChanged("EdgeRadius");
this.OnEdgeRadiusChanged();
}
}
}
}

public partial class ArbitraryProfile : NonCircularProfile
{

private System.Nullable<float> _CenterOfArea_X;

private System.Nullable<float> _CenterOfArea_Y;

private System.Data.Linq.Binary _Drawing;

#region Extensibility Method Definitions
partial void OnLoaded();
partial void OnValidate(System.Data.Linq.ChangeAction action);
partial void OnCreated();
partial void OnCenterOfArea_XChanging(System.Nullable<float> value);
partial void OnCenterOfArea_XChanged();
partial void OnCenterOfArea_YChanging(System.Nullable<float> value);
partial void OnCenterOfArea_YChanged();
partial void OnDrawingChanging(System.Data.Linq.Binary value);
partial void OnDrawingChanged();
#endregion

public ArbitraryProfile()
{
OnCreated();
}

[Column(Storage = "_CenterOfArea_X")]
public System.Nullable<float> CenterOfArea_X
{
get
{
return this._CenterOfArea_X;
}
set
{
if ((this._CenterOfArea_X != value))
{
this.OnCenterOfArea_XChanging(value);
this.SendPropertyChanging();
this._CenterOfArea_X = value;
this.SendPropertyChanged("CenterOfArea_X");
this.OnCenterOfArea_XChanged();
}
}
}

[Column(Storage = "_CenterOfArea_Y")]
public System.Nullable<float> CenterOfArea_Y
{
get
{
return this._CenterOfArea_Y;
}
set
{
if ((this._CenterOfArea_Y != value))
{
this.OnCenterOfArea_YChanging(value);
this.SendPropertyChanging();
this._CenterOfArea_Y = value;
this.SendPropertyChanged("CenterOfArea_Y");
this.OnCenterOfArea_YChanged();
}
}
}

[Column(Storage = "_Drawing", DbType = "VARBINARY(320)")]
public System.Data.Linq.Binary Drawing
{
get
{
return this._Drawing;
}
set
{
if ((this._Drawing != value))
{
this.OnDrawingChanging(value);
this.SendPropertyChanging();
this._Drawing = value;
this.SendPropertyChanged("Drawing");
this.OnDrawingChanged();
}
}
}
}
 
P

Pavel Minaev

Here the code for translating SQL data to domain classes:

IQueryable<DomainModel.Profile> result =
    from p in DC.Profiles
    select
        p.ProfileTyp == (ushort)DomainModel.ProfileType.Rund ?
            new DomainModel.ProfileCircular
            {
                // if next statement is missing
                // exception "has no supported translation to SQL"
                // would be raised when using ProfileTyp as parameter
                // in expression tree for dynamic filtering
                ProfileTyp = (DomainModel.ProfileType)p..ProfileTyp,
                ID = p.ID,
                Name = p.Name,
                Diameter = (p as CircularProfile).Diameter,
                Comment = p.Comment,
                modified = p.modified,
                Version = p.Version
            } :
        p.ProfileTyp == (ushort)DomainModel.ProfileType.Rechteck ?
            new DomainModel.ProfileRectangle
            {
                ProfileTyp = (DomainModel.ProfileType)p..ProfileTyp,
                ID = p.ID,
                Name = p.Name,
                Size_X = (p as RectangleProfile).Size_X,
                Size_Y = (p as RectangleProfile).Size_Y,
                EdgeRadius = (p as RectangleProfile).EdgeRadius,
                Comment = p.Comment,
                modified = p.modified,
                Version = p.Version
            } :
        p.ProfileTyp == (ushort)DomainModel.ProfileType.Sonder ?
            new DomainModel.ProfileArbitrary
            {
                ProfileTyp = (DomainModel.ProfileType)p..ProfileTyp,
                ID = p.ID,
                Name = p.Name,
                Size_X = (p as ArbitraryProfile).Size_X,
                Size_Y = (p as ArbitraryProfile).Size_Y,
                CenterOfArea_X = (p as ArbitraryProfile).CenterOfArea_X,
                CenterOfArea_Y = (p as ArbitraryProfile).CenterOfArea_Y,
                Drawing = (p as ArbitraryProfile).Drawing,
                Comment = p.Comment,
                modified = p.modified,
                Version = p.Version
            } :
            new DomainModel.Profile();
return result;

Alright, I have successfully repro'd your problem with this with some
simplified code. It seems that the problem only manifests itself when
you project the your LINQ to SQL entity objects onto some other POCO
("domain") objects, and then query over the latter.

I can't say specifically why it is the case, but I have a strong
suspicion. LINQ to SQL is still trying to process the entire query,
including the part of it that deals with your projected domain
classes. Now, when you use operators is/as directly on the entity
objects, it knows how to map the check to a comparison against a
discriminator field. However, for your domain classes, it does not
have that mapping, and it is not smart enough to deduce it from your
LINQ query by itself. So effectively any use of operators "is" and
"as" on the domain objects in the query messes it up. You can observe
it if you simplify your generated "where" to just (o => o is
ProfileCircular) - for me at least the analogous code throws another
unobvious exception. It seems that error reporting for that case isn't
particularly good, but otherwise, I can understand why it cannot
handle it.

In any case, if you do queries on DLINQ entities, rather than domain
objects, the problem goes away, so it is what I advise you to do. You
can still have your domain objects, just do the mapping after you've
done all the filtering, and make sure to realize the query by using
AsEnumerable on the IQueryable that you have so that DLINQ doesn't
kick in. Or, perhaps, don't bother with domain objects at all - just
use the entity classes directly. In practice, it is often the best
option.
 
B

Ben Voigt [C++ MVP]

Fritz said:
Hi Pavel

The expression tree in literal is:

Lambda - e => IIF((e Is ProfileCircular), (Convert((e As
ProfileCircular).Diameter) >= 40), True)
Conditional - IIF((e Is ProfileCircular), (Convert((e As
ProfileCircular).Diameter) >= 40), True)
TypeIs - (e Is ProfileCircular)
Parameter - e
GreaterThanOrEqual - (Convert((e As ProfileCircular).Diameter)
MemberAccess - (e As ProfileCircular).Diameter
TypeAs - (e As ProfileCircular)
Parameter - e
Constant - 40
Constant - True

The problem is that the IIF function doesn't short-circuit like AND would.

Not to mention that the else-part of the IIF needs to be FALSE!
 
P

Pavel Minaev

The problem is that the IIF function doesn't short-circuit like AND would..

What IIF function? This isn't VB code snippet, this is just the result
of applying ToString to a lambda expression. IIF in that case is a
basic operator, not a function - C# ?: ternary operator is translated
to IIF, and that one is quite obviously short-circuited (though in
practice, when dealing with IQueryable, it is of course up to the
implementer how to handle that).
 
B

Ben Voigt [C++ MVP]

Pavel Minaev said:
What IIF function? This isn't VB code snippet, this is just the result
of applying ToString to a lambda expression. IIF in that case is a
basic operator, not a function - C# ?: ternary operator is translated
to IIF, and that one is quite obviously short-circuited (though in
practice, when dealing with IQueryable, it is of course up to the
implementer how to handle that).

I had assumed, apparently mistakenly, that it was showing the SQL generated
by LINQ-to-SQL.

And translating ?: to IIF is an obvious error, because they don't have the
same semantics.

In any case, the else-part argument is clearly wrong (shown as true but
should be false).
 
P

Pavel Minaev

I had assumed, apparently mistakenly, that it was showing the SQL generated
by LINQ-to-SQL.

Yep, it's not (since at that point the given instance of Expression<T>
doesn't know that it's going to be passed to an IQueryable
implementation provided by LINQ to SQL).

It's really just a handy condensed representation of the expression
tree, mostly for debugging purposes. For some reason, they've decided
it to be "language agnostic" in that it looks unlike any present .NET
language.
In any case, the else-part argument is clearly wrong (shown as true but
should be false).

I don't think it's relevant to the problem Fritz was having. I did my
repro on a new solution that did not include any of its code, only
following the descriptions instead.
 
F

Fritz

After a few days out of office I'm back.


Ben Voigt said:
Not to mention that the else-part of the IIF needs to be FALSE!

Pavel is right, TRUE or FALSE as else-part does make no difference, I tried
both.

Meanwhile I overcome the problem by flatening my db-schema and entity
classes. I created "all-inclusive-classes" without inheritance to deal with
LINQ-to-SQL.
After all this work it is at least working.
 
F

Fritz

To overcome this issue I'm no longer using inheritance in the domain classes
and DB-schema. But now new trouble is coming up with array indexing while
filtering.

Having domain classes like

public class Tool {
public int? Type { get; set; }
}

public class Kit {
public Tool[] Tools { get; set; }
}

and creating a filter using Expression.ArrayIndex looks like:

kit => (Convert(kit.Tools[0].Type) = 1)

an InvalidOperationException is thrown during runtime saying
"Unrecognized expression node: ArrayIndex".

It seams LINQ-to-SQL is not supporting expressions of type ArrayIndex.
Is there a workaround for this problem?
 
Top