Roslyn: Proposal: A Special Version of nameof That Does Nothing But Ensure A Property Exist

Created on 15 Sep 2016  路  10Comments  路  Source: dotnet/roslyn

Background

First of all, I love Entity Framework due to the fact that it helps detecting damages to a source code whenever a database schema changes, due to the fact that entities are modeled into a C# class

However, that mechanism doesn't really help when you need to do raw SQL queries. (Mainly for performance reason and/or improving flow / readability)

To mitigate this, I've developed a programming technique like this:

if (false)
{
#pragma warning disable CS0162 // Unreachable code detected
    var guard = new List<string>();
    guard.Add(nameof(EmployeeRole.AppRoleId));
    guard.Add(nameof(EmployeeRole.EmployeeId));
    guard.Add(nameof(AppRole.Name));
    guard.Add(nameof(AppRole.AppId));
    guard.Add(nameof(AppRole.AppRoleId));
#pragma warning restore CS0162 // Unreachable code detected
}

return await DB.Database.GetDbConnection().QueryAsync<string>(@"
SELECT R.Name FROM EmployeeRole ER
JOIN AppRole R ON ER.AppRoleId = R.AppRoleId
WHERE (ER.EmployeeId = @employeeId) AND (R.AppId = @appId)", new
{
    employeeId,
    appId
});

This allows raw queries to take advantage of EF entity model properties for ensuring that the column actually exists during compile-time.

Proposal

A syntactic sugar of above technique would be awesome:

guard(EmployeeRole.AppRoleId);
guard(EmployeeRole.EmployeeId);
guard(AppRole.Name);
guard(AppRole.AppId);
guard(AppRole.AppRoleId);

return await DB.Database.GetDbConnection().QueryAsync<string>(@"
SELECT R.Name FROM EmployeeRole ER
JOIN AppRole R ON ER.AppRoleId = R.AppRoleId
WHERE (ER.EmployeeId = @employeeId) AND (R.AppId = @appId)", new
{
    employeeId,
    appId
});

This new syntax:

  • Should not be too difficult to implement. (Due to nameof already existed)
  • Will not break nor slow down existing application (Only for compile-time checking).
  • Helps tremendously for organizing raw queries in source codes.

EDIT:

Or simply let nameof be used without assigning values?

nameof(EmployeeRole.AppRoleId);
nameof(EmployeeRole.EmployeeId);
nameof(AppRole.Name);
nameof(AppRole.AppId);
nameof(AppRole.AppRoleId);
Area-Language Design Discussion

All 10 comments

couldn't you just get away with assertions?
This has the added bonus of working right now, and also not writing code that's unreachable.

    Debug.Assert(nameof(EmployeeRole.AppRoleId) != null);
    Debug.Assert(nameof(EmployeeRole.EmployeeId) != null);
    Debug.Assert(nameof(AppRole.Name) != null);
    Debug.Assert(nameof(AppRole.AppId) != null);
    Debug.Assert(nameof(AppRole.AppRoleId) != null);

If you want your code to be more succinct and more clear, you could get pretty close to what you're proposing without any new syntax. Create a guard() method:

``` c#
public static void guard(params string[] names) {}

Then use it like this:

``` c#
guard(
    nameof(EmployeeRole.AppRoleId),
    nameof(EmployeeRole.EmployeeId),
    nameof(AppRole.Name),
    nameof(AppRole.AppId),
    nameof(AppRole.AppRoleId));

I think this pattern you're using is a very special case, not something many people would use. Because of that, I don't think it should be a language feature.

Thank you for the reply, guys. Those are great suggestions, you guys rock. I'll add them to my repertoire too.

However, it's still too long, I guess? You need to call Debug.Assert, nameof and != null to achieve the goal. Plus, it requires #pragma warning disable RECS0065 // Expression is always 'true' or always 'false' to prevent the warning floods.

And for the method-based one, creating the method for new projects would be a chore, I guess.

I'm still convinced that it should be a language feature. Writing raw queries are part of daily rituals as a developer. By implementing this feature, we can introduce new best practice to the world for managing correctness of raw queries.

You could be lazier of course and write this...

Debug.Assert((Action<EmployeeRole, AppRole>)((e,a)=>null != Tuple.Create(e.AppRoleId, e.EmployeeId, a.Name, a.AppId, a.AppRoleId) );

And in C#7 you can make that even better by using tuple syntax.

You could be lazier of course and write this...

Genius. Bloody genius. I didn't think of that.

Here's even lazier technique:

Action<EmployeeRole, AppRole> guard = (ER, R) =>
{
    Tuple.Create(ER.AppRoleId, ER.EmployeeId, R.Name, R.AppId, R.AppRoleId);
};

Well sure, but you may have an inspection that complains about unused locals. (I know I do in most of my projects). You can combine the former with a Debug.Assert and get your guard in 2 lines. And in C#7, you can drop the Tuple.Create

Will the following

static class Assertions
{
    [Conditional("NEVERMIND")]
    public static void PleaseDoExist(params string[] nameofs) { }
}

... 

using static SomeNamespace.Assertions;

class A
{
    public int B => 0;
    public int C => 0;
    public int D => 0;
}

static class Program
{
    static void Main(string[] args)
    {
        PleaseDoExist(
            nameof(A.B),
            nameof(A.C),
            nameof(A.D));
        ...
    }
}

work?

As for me, I'd prefer something like

[Conditional("NEVERMIND")]
[AttributeUsage(AttributeTargets.All)]
public sealed class WishYouWereHereAttribute : Attribute
{
    public WishYouWereHereAttribute(params string[] nameofs) { }
}

Or simply let nameof be used without assigning values?

A variant of this idea works already. Since C# 7.0, you can assign to a discard.
For instance, _ = nameof(EmployeeRole.AppRoleId);

A variant of this idea works already. Since C# 7.0, you can assign to a discard.

Whoa neat. This is amazing, thank you.

EDIT: Will there be any performance penalty for using discard + nameof? Or will the statements be actually 'removed' when the application is being compiled?

Glad to hear.
It looks like the optimizer doesn't emit it. See TryRoslyn example.

Consider closing the thread if the proposal is no longer worthwhile.

Was this page helpful?
0 / 5 - 0 ratings