I have long process and sometimes I want to return process status. I have the following code:

public delegate Task DeviceOnSureMdmResetIMEIHandler(TabletWithoutDeviceIdDto sureMdmTablet, string oldIMEI);
public delegate Task LocalTabletGroupIsNotFoundHandler(TabletWithoutDeviceIdDto sureMdmTablet);
public delegate Task ExistingDeviceIsNotTabletHandler(Device device);
public delegate Task ErrorCombiningDevicesHandler(string result);

public interface ISureMdmTabletService
{
    List<TabletWithoutDeviceIdDto> Tablets { get; set; }

    event DeviceOnSureMdmResetIMEIHandler DeviceOnSureMdmResetIMEIEvent;
    event LocalTabletGroupIsNotFoundHandler LocalTabletGroupIsNotFoundEvent;
    event ExistingDeviceIsNotTabletHandler ExistingDeviceIsNotTabletEvent;
    event ErrorCombiningDevicesHandler ErrorCombiningDevicesEvent;

    List<string> DeviceIsChanged(TabletWithoutDeviceIdDto newItem, Device dbDevice);
    string ImeiIsChanged(TabletWithoutDeviceIdDto newItem, Tablet dbTablet);
    List<string> TabletIsChanged(TabletWithoutDeviceIdDto newItem, Tablet dbTablet);
    bool ShouldPhoneNumberBeUpdated(TabletWithoutDeviceIdDto newItem, Device device);
    bool ShouldICCIDBeUpdated(TabletWithoutDeviceIdDto newItem, Device device);
    bool ShouldIMSIBeUpdated(TabletWithoutDeviceIdDto newItem, Device device);

    void UpdateAlreadyExistingTablets();
    void AddNewTablets();
    void DeletedTablets();
    Task DuplicateImeiRecordsAsync();

    Task<UpdateTabletsFromSureMdmResultDto> UpdateTabletsSaveChangesAsync();
}


public class SureMdmTabletService : ISureMdmTabletService
{
    public event DeviceOnSureMdmResetIMEIHandler DeviceOnSureMdmResetIMEIEvent;
    public event LocalTabletGroupIsNotFoundHandler LocalTabletGroupIsNotFoundEvent;
    public event ExistingDeviceIsNotTabletHandler ExistingDeviceIsNotTabletEvent;
    public event ErrorCombiningDevicesHandler ErrorCombiningDevicesEvent;

    private List<TabletWithoutDeviceIdDto> _tablets;
    public List<TabletWithoutDeviceIdDto> Tablets
    {
        get
        {
            return _tablets;
        }
        set
        {
            _tablets = value;
        }
    }
    private List<string> _tabletsIMEI { get; set; }
    private List<string> _tabletsIds { get; set; }

    private List<Tablet> _alreadyExistsTablets { get; set; }
    private List<Tablet> _allTablets { get; set; }


    private readonly ICombine2DevicesIntoOneService _combine2DevicesIntoOneService;
    private readonly DataContext _dbContext;
    private readonly IMapper _mapper;
    private DeviceModel _deviceModel;

    public SureMdmTabletService(DataContext dbContext, ICombine2DevicesIntoOneService combine2DevicesIntoOneService)
    {
          // .....
    }

    public List<string> DeviceIsChanged(TabletWithoutDeviceIdDto newItem, Device dbDevice)
    {
          // .....
    }

    public string ImeiIsChanged(TabletWithoutDeviceIdDto newItem, Tablet dbTablet)
    {
          // .....
    }

    public List<string> TabletIsChanged(TabletWithoutDeviceIdDto newItem, Tablet dbTablet)
    {
          // .....
    }

    // .....


    public void UpdateAlreadyExistingTablets()
    {
         **DeviceOnSureMdmResetIMEIEvent.Invoke(...);**
    }
}

and wrapper about it:

public interface ISureMdmTabletUpdateCommandService
{
    Task<UpdateTabletsFromSureMdmResultDto> CommandAsync(List<TabletWithoutDeviceIdDto> tablets);
}

public class SureMdmTabletUpdateCommandService : ISureMdmTabletUpdateCommandService
{
    private readonly ISureMdmTabletService _sureMdmTabletService;

    public SureMdmTabletUpdateCommandService(ISureMdmTabletService sureMdmTabletService)
    {
        _sureMdmTabletService = sureMdmTabletService;
    }

    public async Task<UpdateTabletsFromSureMdmResultDto> CommandAsync(List<TabletWithoutDeviceIdDto> tablets)
    {
        _sureMdmTabletService.Tablets = tablets;
        _sureMdmTabletService.UpdateAlreadyExistingTablets();

        _sureMdmTabletService.AddNewTablets();

        _sureMdmTabletService.DeletedTablets();

        await _sureMdmTabletService.DuplicateImeiRecordsAsync();

        return await _sureMdmTabletService.UpdateTabletsSaveChangesAsync();
    }
}

this wrapper is used by client app:

                var resultTablets = await _sureMdmTabletUpdateCommandService.CommandAsync(tablets);

I want to subscribe on events from client app like this:

_sureMdmTabletUpdateCommandService.DeviceOnSureMdmResetIMEIEvent += 

but ISureMdmTabletUpdateCommandService does not have, nested class has it. How to throw these events carefully and without dummy intermediate events inside ISureMdmTabletUpdateCommandService ?


Solution 1:

Having wrapper requires you to have all the infrastructure on it and you are not required to have full event there just a intermediate methods to attach and detach so you can do explicit event implementation:

object objectLock = new Object();

event DeviceOnSureMdmResetIMEIHandler DeviceOnSureMdmResetIMEIEvent
    {
        add
        {
            lock (objectLock)
            {
                _sureMdmTabletService.DeviceOnSureMdmResetIMEIEvent += value;
            }
        }
        remove
        {
            lock (objectLock)
            {
                _sureMdmTabletService.DeviceOnSureMdmResetIMEIEvent -= value;
            }
        }
    } 

the lock is required as += and -= operations are not atomic its read modify write.


Having that sad I do not recommend using events at all. I would just passed as a parameter an Action<TabletWithoutDeviceIdDto, string> not even declaring custom delegate for it. Its just simpler approach and it can event support asynchronous operation if you use Func<TabletWithoutDeviceIdDto, string, Task> in contrast to events.