-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Description
FindAsync currently takes a params object array as its first parameter and cancellation token as its second parameter. In entities that use struct's as keys (which is probably the norm for most entities) it is easy to mess up the parameters to this method.
For example, given the following entity context:
public class ApplicationContext : DbContext {
public ApplicationContext(DbContextOptions<ApplicationContext> options)
: base(options) {
}
public DbSet<Blog> Blogs { get; set; } = default!;
}
public sealed class Blog {
public Guid BlogId { get; set; }
public string Name { get; set; } = default!;
}
Attempt One
The first attempt to use FindAsync
would probably look like this, which will compile but is actually passing both parameters to the object array and not passing the cancellation token to the correct parameter:
var blog = await db.Blogs.FindAsync(blogId, cancellationToken);
This results in the following error:
ArgumentException:
Entity type 'Blog' is defined with a single key property, but 2 values were passed to the 'DbSet.Find' method.
Attempt Two
The second attempt to use FindAsync
would probably be to change the first parameter to be an array so the type system passes the correct parameters:
var blog = await db.Blogs.FindAsync(new[] { blogId }, cancellationToken);
This however does not work because the array is not specifically an object[]
, so you still get the following error:
ArgumentException:
Entity type 'Blog' is defined with a single key property, but 2 values were passed to the 'DbSet.Find' method.
NOTE: If BlogId were a reference type (i.e. string) instead of a value type, this usage actually works.
Correct Usage
var blog = await db.Blogs.FindAsync(new object[] { blogId }, cancellationToken);
Possible Solutions
Solution 1
The first, an my prefered, solution would be to add some checks in FindAsync
to detect if the last item in the keyValues
parameter is a CancellationToken
if the count of keys in the entity being queried does not match the count of keys passed in. In this scenario, if the last item is a CancellationToken
, use it as a CancellationToken
instead of part of the key.
Solution 2
Add analyzers to detect this incorrect usage of the API to provide build time errors instead of runtime errors.