Henrique Pombo
Henrique Pombo

Reputation: 713

Implementing an Interface on a class with Dependency Injection

I'm developing a Blazor app and somewhere I'm implementing an interface:

public class UserData : IUserData
{
    private readonly ISqlDataAccess _db;

    public UserData(ISqlDataAccess db)
    {
        _db = db;
    }

    public void SomeFunction ()
    {
        ...
    }
}
public interface IUserData
{
    void SomeFunction();
}

While on .razor I can do: @inject IUserData UserData; and ((UserData)UserData).SomeFunction();; I'm failing to discover how to do it on .cs file.

//There is no such constructor and If I create a new one, then I won't get the _db dependecy injection
IUserData userDate = new UserData();
userDate.SomeFunction();

Edit

So now, when I'm calling the method from the .cs file, the app freezes; it doesn't throw an error and I am able to refresh the page, so it seems it's stuck on the call to the db; but if I call it from the .razor it works flawlessy.

.cs

public AccountService(IUserData userData)
{
    _userData = userData;
}
...
public async Task<bool> Validate(string userId, string password)
{
    ...
    try
    {
        List<UserModel> users = new List<UserModel<();
        users = await _userData.GetUsers();
        //NEVER GETS HERE
        return true;
    }
    catch (Exception ex)
    {
         return false;
    }
    ...
}

.razor

@inject IUserData _db;

@code {
    private List<UserModel> users;
    ...
    protected override async Task OnInitializedAsync()
    {
        users = await _db.GetUsers();
    }
    ...

UserData

    public class UserData : IUserData
    {
        private readonly ISqlDataAccess _db;

        public UserData(ISqlDataAccess db)
        {
            _db = db;
        }

        public Task<List<UserModel>> GetUsers()
        {
            string sql = "Select *from dbo.Users";
            return _db.LoadData<UserModel, dynamic>(sql, new { });
    }
    ...
}

IUserData

    public interface IUserData
    {
        Task<List<UserModel>> GetUsers();
        ...
    }

Edit2 It turns out I was missing an await when calling Validate() service, and thus not running it asynchronous.

Upvotes: 0

Views: 1244

Answers (2)

Skuerke
Skuerke

Reputation: 23

In Startup.cs you can register your interface and implementation;

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddScoped<IUserData, Userdata>();
    }

Then you can use the interface in a class:

public class TestClass{
  private IUserData _userData;
  public TestClass(IUserData userdata){
    _userData = userdata;
  }
}

Upvotes: 2

Yamuk
Yamuk

Reputation: 769

At some point in the program, you need to setup dependency injection. This is most common to do in the ConfigureServices method in Startup.cs by convention.

public class Startup
{
    public IConfiguration Configuration { get; }

    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
           .SetBasePath(env.ContentRootPath)
           .AddJsonConfigurationProvider("appsettings.json", optional: false, reloadOnChange: true)
           .AddEnvironmentVariables();
            Configuration = builder.Build();
    }
    public void ConfigureServices(IServiceCollection services)
    {
         services.AddTransient <ISqlDataAccess, SqlDataAccess>(); //Second argument is the implementation of the interface
         services.AddTransient <IUserData, UserData>();
    }
}

You need to pass the ISqlDataAccess to the constructor of the UserData but you had it covered already.

public class UserData : IUserData
{
    private readonly ISqlDataAccess _db;

    public UserData(ISqlDataAccess db)
    {
        _db = db;
    }
    //...
}

Then you need to pass your IUserData to your objects via constructors:

public class ClassWithIUserDataDependency {
    private IUserData _userData;
    public ClassWithIUserDataDependency (IUserData userData) {
        _userData = userData;
    }

    //rest of the class
}

One note: You would need to pass IUserData to all dependency classes. Based on the name, this looks like a POCO object (If it is not, don't mind this comment) If this is a POCO class, or anything representing a DTO or Data, then it is better to separate db from it and allow users to just new it. If it is not, you may want to change its name.

Upvotes: 3

Related Questions