Home > Mobile >  How to use interface default implementation in C#
How to use interface default implementation in C#

Time:12-13

I have classes which are inherits from base class.

public class Car : BaseEntity
{
    public string Name { get; private set; }

    public Car(string name)
    {
        Name = name;
        // update method goes here
    }
}


public abstract class BaseEntity
{
    public Guid Id { get; } = Guid.NewGuid();
}

I want to have an interface or something else where I can audit this entity changes. I made an interface and added default implementation like below :

public interface IAuditable
{
    // userNameProvider goes here
    DateTime CreatedTime { get; set; }
    Guid CreatedBy { get; set; }

    void Audit()
    {
        CreatedTime = DateTime.UtcNow;
        // CreatedBy =  userNameProvider.GetUserId();
    }
}

But the issue is that I need to cast my Car into this interface in order to call Audit method. As it's not forced to implement in the Car class, I guess I can just forget it or something else. I want a solution which could be reusable for all entities deriving from this interface just by calling one method and not to be afraid to forget Audit method. I'd highly appreciate your suggestions. Thanks

Example code what is working right now but need to simplify :

var car = new Car("bmw");

Console.WriteLine(car.CreatedTime);

public class Car : BaseEntity, IAuditable
{
    public string Name { get; private set; }
    public DateTime CreatedTime { get; set; }

    public Car(string name)
    {
        Name = name;
        (this as IAuditable).Audit();
        // update method goes here
    }
}

I mentioned above what I did and my expectation, please take a look :)

CodePudding user response:

You can override your DbContext SaveChangesAsync method

public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
{
    foreach (var e in ChangeTracker.Entries()
                                   .Where(i => i.State == EntityState.Added
                                            || i.State == EntityState.Modified)
                                   .Select(i => i.Entity)
                                   .OfType<IAuditable>())
    {
        e.Audit();
    }

    return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}

This will ensure that all IAuditable entities modified through your DB Context will have their Audit method called.

CodePudding user response:

You cannot use default interface methods directly if you don't implement them in the concrete class. However, you can add an extension method to make calling the default interface method easier:

public static class AuditableExtensions
{
    public static void Audit(this IAuditable auditable)
    {
        auditable.Audit();
    }
}

// usage:

Car car = new Car();
car.Audit();

// or

public class Car : BaseEntity, IAuditable
{
    public Car()
    {
        this.Audit();
    }
}

CodePudding user response:

You can use an external processing class like this:

public class Car : BaseEntity, IAuditable
{
    public string Name { get; private set; }

    public Car(string name)
    {
        Name = name;
        Auditor.Audit(this);
    }
}

public static class Auditor
{
    public static void Audit(IAuditable entity)
    {
        entity.Audit();
    }
}
  • Related