Three-way join in Entity Framework Core to retrieve all roles that a user has
For this isolated piece of code, this is the way to go. Generally, in ORMs that support LINQ, navigation properties (like ur.Roles) should be used wherever possible. Usingjoin should be reserved for custom joins where no navigation properties exist.

Reasons:  


Joins are far more verbose than navigation properties, making code harder to read and causing repetitive code.
Joins are error-prone. It's easy to match the wrong fields. A navigation property, once configured correctly, will always generate a correct SQL join.
Navigation properties, if well-named, are more expressive. In a join the cardinality of a relationship isn't always obvious. In navigation properties it is. You know what you're looking at when reading Role vs Roles. Unfortunately, sometimes people tend to be sloppy in this area and they just use the generated class and property names as an ORM generated them, which may result in any mixture of plural class names, plural reference property names, and singular collection property names.


One exception to this rule is when you want to overrule the ORM's SQL generation. Entity Framework will generate an OUTER JOIN for non-required navigation properties (i.e. where the "1"-side is optional, 0..1-n, 0..1-1). There may be cases where INNER JOIN performs better and the results with null navigation properties aren't interesting anyway. In those cases, using a manual join, overriding a navigation property, could be considered.

Side note: as said in a comment, it's recommended to use disposable objects, like a DbContext, in a using statement.