1

I have the following class derived from entity framework class as follows.

internal class MyInitializer : CreateDatabaseIfNotExists<ConfigurationDbContext>
{
    protected override async void Seed(ConfigurationDbContext context)
    {
        await DbHelper.AddSampleDataAsync(context);
        base.Seed(context);
    }
}

I get the following compiler error when I change "void" to "async Task".

return type must be 'void' to match overridden member

async/await best practices tell you to almost always return async Task instead of void. Just wanted to make sure my usage of void here is one of those acceptable scenarios for using void. What other options do I have anyways?

SamDevx
  • 2,268
  • 4
  • 32
  • 47
  • 2
    Your overriding a method, thus you have to have the same signature of the method as the overridden one. – Håkan Fahlstedt May 14 '15 at 19:18
  • Will the type function properly if you do the work asynchronously and anyone calling `Seed` (either through this type or through the parent type) going to function properly if it returns before the work is actually done, and without providing any means of determining the result (or any errors) of this operation? If yes, leave the async void method, if no, you have to either not do the operation asynchronously, or not meet this signature. – Servy May 14 '15 at 19:19
  • EF expects this to happen synchronously, so you should add your seed data synchronously. – Charles Mager May 14 '15 at 19:23
  • @CharlesMager, I think you are right. I also think EF should provide SeedAsync(). – SamDevx May 14 '15 at 19:41
  • @Servy, since EF does not provide SeedAsync(), JNYRanger's solution seems pretty good, don't you think? – SamDevx May 14 '15 at 19:48
  • @SamDevx I have no idea if it will work for you, given your specific situation. Perhaps it will, perhaps it won't. – Servy May 14 '15 at 19:53
  • This question is similar and implies it has potential to deadlock: http://stackoverflow.com/questions/27498921/creating-asp-net-identity-user-in-seed-method-of-db-initializer – Charles Mager May 14 '15 at 19:53

2 Answers2

2

In C#, overridden members must have exactly the same signature as the method they are overriding. You simple can't override a method with another method of a different return type.

Because the base class expect the method to run syncronously, you would have to wait for the async method to complete before returning.

internal class MyInitializer : CreateDatabaseIfNotExists<ConfigurationDbContext>
{
    protected override void Seed(ConfigurationDbContext context)
    {
        DbHelper.AddSampleDataAsync(context).Wait();
        base.Seed(context);
    }
}
Jason Watkins
  • 3,766
  • 1
  • 25
  • 39
  • And he (at least plausibly) can't match the return type and have it function properly either. This is a paradox. – Servy May 14 '15 at 19:21
  • @Servy He certainly can have if function synchronously. Whether that counts are "properly" up for debate. – Jason Watkins May 14 '15 at 19:24
  • We have no idea if he has a synchronous implementation, or if he does, if it's appropriate for this operation to be performed asynchronously here. Perhaps it is, perhaps it isn't. – Servy May 14 '15 at 19:26
  • @Servy You can always get a synchronous implementation by simply `Wait`ing the task returned by the async implementation. – Jason Watkins May 14 '15 at 19:27
  • Not in certain paradigms you can't, for example winphone/silverlight apps disable that ability, and in others the code wouldn't function if you tried to perform the operation synchronously (for example a winform/wpf app might freeze if this is done synchronously on the UI thread). – Servy May 14 '15 at 19:27
  • @JasonWatkins, thanks for feedback. The base class is Entity Framework (EF) class and clearly I cannot change its code. EF will call my MyInitializer.Seed() when conditions are met. EF will never call MyInitializer.SeedAsync() even if I were to include it. So, in order for me to asynchronoulsy seed the database (a potentially long running task) using the EF hooks, do you think that the only way would be for Microsoft to provide SeedAsync() in addition to Seed()? – SamDevx May 14 '15 at 19:39
  • @SamDevx Yes, that's essentially right. If you want to seed the db asynchronously, you will have to do it manually, without using the EF hooks. If you want to use the EF hooks, then you are restricted to doing the seed operation synchronously until the EF team adds a `SeedAsync` method. – Jason Watkins May 14 '15 at 19:42
  • @JasonWatkins, your code solution above which is to Wait() on awaitable before calling the base is equivalent to JNYRanger's solution below. In either case though, is there a chance of deadlock? – SamDevx May 14 '15 at 20:22
  • @SamDevx Technically that depends on the implementation of `AddSampleDataAsync`, but as long as you are not manually taking any locks inf `AddSampleDataAsync` it is very, very unlikely that there is any chance of a deadlock. – Jason Watkins May 14 '15 at 20:34
  • @JasonWatkins All it would take is there to be a non-null synchronization context and for the async method to use `await` in its implementation. – Servy May 14 '15 at 21:10
2

When overriding a method it must have the same signature as the original method of the base class (or an interface / abstract class). I would imagine based on this question the expected behavior of Seed() is a synchronous operation. Therefore it's not a good idea to override it and completely change the behavior to asynchronous.

Instead you should create a second method that is asynchronous, and keep the synchronous one.

This method below (as suggested by @DanielEarwicker) allows you to have BOTH a synchronous and asynchronous version by waiting for the asynchronous operation to complete in the synchronous version of Seed()

internal class MyInitializer : CreateDatabaseIfNotExists<ConfigurationDbContext>
{
    protected override void Seed(ConfigurationDbContext context)
    {
        SeedAsync(context).Wait();
    }

    protected async Task SeedAsync(ConfigurationDbContext context)
    {
        await DbHelper.AddSampleDataAsync(context);
        base.Seed(context)
    }
}
JNYRanger
  • 6,829
  • 12
  • 53
  • 81
  • The point of overriding `Seed` is to carry out an initialization task on the database. One way to improve this answer would be to have `Seed` call `SeedAsync` and ensured that it had completed before returning from `Seed`. – Daniel Earwicker May 14 '15 at 19:23