---
title: Partial Implementation
---

##Using AdminUIs base stores
Samples of the below code can be found on our [GitHub](https://github.com/RockSolidKnowledge/Samples.CustomIdentityStore/tree/main/Duende)

For this part of the documentation the code in [Base Installation](https://github.com/RockSolidKnowledge/Samples.CustomIdentityStore/tree/main/Duende/Base%20Installation) project will be considered the "Before".

###Extending Functionality

The example functionality extension will be performed on the `CreateUser` method on the `ISSOUserStore`.

For this, you must:

1. Create a class that derives from `SSOUserStore`
2. Create a factory that creates this class
3. Overwrite the `CreateUser` method and include a notifier

###Creating a new class
- To start, create a new class in your project call `CustomUserStore.cs`
- Make your class extend the base class `SSOUserStore`
- Create a constructor that calls `base` with the required interfaces
    - Because the AdminUI DI container has already registered the services needed to extend `SSOUserStore` you don't need to do any DI configuration on your side, you only need to import the correct interfaces.

After these steps you will end up with a class that looks like the following:

```c#
public class CustomUserStore : SSOUserStore
{
    public CustomUserStore(IIdentityUnitOfWorkFactory factory, ILookupNormalizer normaliser, IUserQueryFactory userQueryFactory) : base(factory, normaliser, userQueryFactory)
    {
    }
}
```

To use this class instead of using the default implementation, you now need to provide an implementation of the `ISSOStoreFactory` that returns this implementation of the `ISSOUserStore` and not AdminUI's

- In a new file create a class called `CustomIdentityFactory` that implements `ISSOStoreFactory`
- In the constructor, pass in classes needed to create the new `CustomUserStore` and `CustomIdentityFactory`
- Override the `CreateUserStore` method, returning the `CustomUserStore`

After these steps you will have a new class that looks like this:

```c#
public class CustomStoreFactory : ISSOStoreFactory
{
    private readonly IIdentityUnitOfWorkFactory _factory;
    private readonly ILookupNormalizer _normaliser;
    private readonly IUserQueryFactory _userQueryFactory;

    public CustomStoreFactory(IIdentityUnitOfWorkFactory factory, ILookupNormalizer normaliser, IUserQueryFactory userQueryFactory)
    {
        _factory = factory ?? throw new ArgumentNullException(nameof(factory));
        _normaliser = normaliser ?? throw new ArgumentNullException(nameof(normaliser));
        _userQueryFactory = userQueryFactory ?? throw new ArgumentNullException(nameof(userQueryFactory));
    }
    
    public ISSOUserStore CreateUserStore()
    {
        return new CustomUserStore(_factory, _normaliser, _userQueryFactory);
    }

    public ISSORoleStore CreateRoleStore()
    {
        return new SSORoleStore(_factory, _normaliser, _userQueryFactory);
    }

    public ISSOClaimTypeStore CreateClaimTypeStore()
    {
        return new SSOClaimTypeStore(_factory, _normaliser);
    }
}
```

Now register this factory during pipeline initialisation. In your `Program.cs` or `Startup.cs`, update the `UseAdminUI` call by adding `WithIdentityStore`.
This method accepts a type that needs to implement `ISSOStoreFactory`

```c#
var builder = WebApplication.CreateBuilder(args);

builder.Services
        .AddAdminUI()
        .WithIdentityStore<CustomStoreFactory>();
```

If you run the code and put a breakpoint in your `CustomUserStore` constructor, you can see that you are now using your own code. Now you can extend it with your own functionality

Now: 
- Create a `NotificationService` interface that has a `Dispatch` method, create a class that inherits it and register it in the DI container
- Pass it into `CustomUserStore`. You will also need to include it in the `CustomStoreFactory` constructor.
- Overwrite the `CreateUser` method to use the notification service.

Now, when AdminUI creates a user, it will also call off to the new `NotificationService`.

```c#
public class CustomUserStore : SSOUserStore
{
    private readonly INotificationService _notificationService;

    public CustomUserStore(INotificationService notificationService, IIdentityUnitOfWorkFactory factory, ILookupNormalizer normaliser, IUserQueryFactory userQueryFactory) : base(factory, normaliser, userQueryFactory)
    {
        _notificationService = notificationService ?? throw new ArgumentNullException(nameof(notificationService));
    }

    public override Task<ISSOUser> CreateUser(ISSOUser user)
    {
        var createdUser = base.CreateUser(user);
        
        _notificationService.Dispatch("CreateUser");
        
        return createdUser;
    }
}
```


The finished code can be found [here](https://github.com/RockSolidKnowledge/Samples.CustomIdentityStore/tree/main/Duende/Extending)

### Overriding certain calls
In this section you will create a stub service to show how you overwrite certain methods if you need your own logic surrounding a method.

A good example of this is the Delete method in the `ISSOUserStore`. By default, AdminUI performs a 'soft' deletion when deleting a user, wherein the `IsDeleted` flag is set to true. 
However, there is functionality within the `UserManager` used by AdminUI to perform a hard deletion wherein a user is completely removed from the database.

If you preferred a user to be completely removed during deletion: 

- Create a class that inherits from `SSOUserStore` like above
- Overwrite `Delete` method  
- Initialise an instance of the unit of work
- Call the `HardDelete` method on the `UserManager` 

```c#
public class CustomUserStore : SSOUserStore
{
    private readonly INotificationService _notificationService;
    private readonly IIdentityUnitOfWorkFactory _factory;

    public CustomUserStore(INotificationService notificationService, IIdentityUnitOfWorkFactory factory, ILookupNormalizer normaliser, IUserQueryFactory userQueryFactory) : base(factory, normaliser, userQueryFactory)
    {
        _notificationService = notificationService ?? throw new ArgumentNullException(nameof(notificationService));
        _factory = factory ?? throw new ArgumentNullException(nameof(factory));
    }

    ...

    public override async Task DeleteUser(ISSOUser user)
    {
        if (user == null)
        {
            throw new ArgumentNullException(nameof(user));
        }

        using var uow = _factory.Create();
        var databaseUser = user.ToIdentityExpressUser();
        await uow.UserManager.HardDeleteAsync(databaseUser);
    }
}
```

As a result, when AdminUI's service layer calls the `Delete` method on the user store, instead of the default delete functionality, your new functionality will be called and the user will be completely removed 
from the database.
