I'm using .Net framework 4.7.3, with Entity Framework and LINQ.
I'm trying to populate a new class from the database with one of two sub-classes using LINQ, but I'm running into an error with the syntax I'm using and wondered how I could possibly get around it.
The error is
CS8400: Feature 'target-typed conditional expression' is not available in C#7.3
Here's a simplified but complete version:
LINQ query (The problem):
return repo.Find() // Proprietary method returning IQueryable<T>
.Select(x => new PostSnippet
{
Route = x.HasParams
? new DynamicDbRoute {
// Properties
}
: new StaticDbRoute {
// Properties
}
});
Class I'm wanting to populate:
public sealed class PostSnippet
{
public string AltText { get; internal set; }
public string AnchorText { get; internal set; }
public string Image { get; internal set; }
public int PostCount { get; internal set; }
public string Title { get; internal set; }
public IDbRoute Route { get; internal set; }
}
Interface:
public interface IDbRoute
{
string Url { get; }
}
Class Variants:
internal sealed class DynamicDbRoute : DbRoute, IDbRoute
{
internal int NodeId { get; set; }
internal ICollection<RouteParam> RouteParams { get; set; }
internal string TopicName { get; set; }
public override string GetRouteUrl()
{
// Implementation
}
}
internal sealed class StaticDbRoute : DbRoute, IDbRoute
{
public override string GetRouteUrl()
{
// Implementation
}
}
Base class:
internal abstract class DbRoute
{
private string _url;
public string Url => _url ?? (_url = GetRouteUrl());
public string RouteName { get; set; }
public abstract string GetRouteUrl();
}
I'm currently trying to get this to work so it might not be the perfect solution - feel free to chip in - but primarily I need to get a working solution for the query. Any help appreciated
CodePudding user response:
The problem is that C# cannot determine the return type of the ternary expression in C# 7.3. I.e., the compiler is not sure whether the common base type of DynamicDbRoute
and StaticDbRoute
should be DbRoute
or IDbRoute
.
You can help the compiler by casting one of the values to the expected common base type like this:
return repo.Find()
.Select(x => new PostSnippet {
Route = hasParams
? (IDbRoute)new DynamicDbRoute { // Added: (IDbRoute)
// Properties
}
: new StaticDbRoute {
// Properties
}
});
Now, the C# 7.3 compiler is happy.
The Target-Typed Conditional Expressions have been added in C# 9.0. In C# 9 , the compiler looks at the type of the target (the type of the PostSnippet.Route
property in this case, which is IDbRoute
) to disambiguate the best common base type.
Note, you can use this new feature (and many more) in older Framework versions if you set <LangVersion>latest</LangVersion>
in the project file. See: C# language versioning. You also will also need Microsoft Visual Studio 2019 . See: Minimum SDK version needed to support all language features
CodePudding user response:
why not consider using Inheritance? check out this article for more details.
so that instead of
public IDbRoute Route { get; internal set; }
change it to:
public DbRoute Route { get; internal set; }
than Assuming you have your EF configured with inheritance in mind you can use the Include action as follow:
var entity = repo.Find().Include(e => e.Route).FirstOrDefault();
// now you can check which type you have
if (entity.Route is DynamicDbRoute dynamicDbRoute){
// ... code
}
CodePudding user response:
This could be the solution:
bool hasParams = true; // Temp
if(hasParams)
{
return repo.Find() // Proprietary method returning IQueryable<T>
.Select(x => new PostSnippet
{
Route = new DynamicDbRoute {
// Properties
}
});
}
else
{
return repo.Find() // Proprietary method returning IQueryable<T>
.Select(x => new PostSnippet
{
Route = new StaticDbRoute {
// Properties
}
});
}