Skip to content

Controllers BasicCrudIndexActionHandler

Arsenty Politov edited this page Mar 15, 2019 · 1 revision

BasicCrudIndexActionHandler

Action handler that implements controller action that can be used to render Index page with list of entities.

Generic parameters

  • TIdentifier - type of identifier of the entity.
  • TEntity - data model of the entity that is stored in the repository.
  • TIndexViewModel - view model that represents entire Index page.
  • TIndexItemModel - view model that represents a single entity within an Index page.

Overrides

  • GetAllowedEntityProperties - returns list of properties of the entity type that user has access to.
  • PrepareItemsQuery - prepares a query for entities.
  • ConvertEntitiesToIndexItemModel - converts a list of data model entities to list of view model entities.
  • GetIndexViewResult - returns final action result that is executed by ASP.NET.

Basic flow

  1. Validate permissions to access the Index page itself.
  2. Prepare read query. Can be overridden by PrepareItemsQuery.
  3. Apply permissions-based query filter.
  4. Execute query to retrieve list of data model entities.
  5. Get list of allowed properties. Can be overridden by GetAllowedEntityProperties.
  6. Convert list of data model entities to view model entities. Can be overridden by ConvertEntitiesToIndexItemModel
  7. Creates index page view model and populates its items list with view model entities.
  8. Constructs and returns action result.

Example

Basic usage

public class Country
{
    public Guid Id { get; set; }
    public String Name { get; set; }
    public Currency Currency { get; set; }
}

public class CountriesController : Controller
{
    public CountriesController(IEntityControllerServices controllerServices)
    {
        var permissionsValidator = new DefaultEntityPermissionsValidator<Country>(
            permissionsHub: this.ControllerServices.PermissionsHub,
            typeManagerPath: "/Domain/Country",
            entityManagerPath: "/Domain/Country/Entities/{entity:Country}",
            propertyManagerPath: "/Domain/Country/Properties/{property:String}");

        this.IndexHandler = new BasicCrudIndexActionHandler<Guid, Country, DefaultEntityIndexModel<Country>, Country>(this, controllerServices, permissionsValidator);
    }

    protected BasicCrudIndexActionHandler<Guid, Country, DefaultEntityIndexModel<Country>, Country> IndexHandler { get; }

    public Task<IActionResult> Index() => this.IndexHandler.Index();
}

Simple overrides

public class CountriesController : Controller
{
    public CountriesController(IEntityControllerServices controllerServices)
    {
        var permissionsValidator = new DefaultEntityPermissionsValidator<Country>(
            permissionsHub: this.ControllerServices.PermissionsHub,
            typeManagerPath: "/Domain/Country",
            entityManagerPath: "/Domain/Country/Entities/{entity:Country}",
            propertyManagerPath: "/Domain/Country/Properties/{property:String}");

        this.IndexHandler = new BasicCrudIndexActionHandler<Guid, Country, DefaultEntityIndexModel<Country>, Country>(this, controllerServices, permissionsValidator);
        this.IndexHandler.Overrides.PrepareItemsQuery = this.PrepareItemsQuery;
    }

    protected BasicCrudIndexActionHandler<Guid, Country, DefaultEntityIndexModel<Country>, Country> IndexHandler { get; }

    public Task<IActionResult> Index() => this.IndexHandler.Index();

    // This override can be used to eager-load required entities, change ordering, filter, etc.
    private Task<IQueryable<Country>> PrepareItemsQuery()
    {
        var query = this.Repository.Query<Country>("Currency")
            .OrderBy(x => x.Name);

        return Task.FromResult<IQueryable<Country>>(query);
    }
}

More complex overrides

public class CountryViewModel
{
    public CountryViewModel(Country country)
    {
        this.Id = country.Id;
        this.Name = country.Name;
        this.CurrencyName = country.Currency.Name;
    }

    public Guid Id { get; set; }
    public String Name { get; set; }
    public String CurrencyName { get; set; }
}

public class CountriesController : Controller
{
    public CountriesController(IEntityControllerServices controllerServices)
    {
        var permissionsValidator = new DefaultEntityPermissionsValidator<Country>(
            permissionsHub: this.ControllerServices.PermissionsHub,
            typeManagerPath: "/Domain/Country",
            entityManagerPath: "/Domain/Country/Entities/{entity:Country}",
            propertyManagerPath: "/Domain/Country/Properties/{property:String}");

        this.IndexHandler = new BasicCrudIndexActionHandler<Guid, Country, DefaultEntityIndexModel<CountryViewModel>, CountryViewModel>(this, controllerServices, permissionsValidator);
        this.IndexHandler.Overrides.PrepareItemsQuery = this.PrepareItemsQuery;
        this.IndexHandler.Overrides.ConvertEntitiesToIndexItemModel = this.ConvertEntitiesToIndexItemModel;
    }

    protected BasicCrudIndexActionHandler<Guid, Country, DefaultEntityIndexModel<CountryViewModel>, CountryViewModel> IndexHandler { get; }

    public Task<IActionResult> Index() => this.IndexHandler.Index();

    // This override can be used to eager-load required entities, change ordering, filter, etc.
    private Task<IQueryable<Country>> PrepareItemsQuery()
    {
        var query = this.Repository.Query<Country>("Currency")
            .OrderBy(x => x.Name);

        return Task.FromResult<IQueryable<Country>>(query);
    }

    private Task<ICollection<CountryViewModel>> ConvertEntitiesToIndexItemModel(ICollection<Country> entities, String[] allowedProperties)
    {
        var result = entities.Select(x => new CountryViewModel(x)).ToList();
        return Task.FromResult<ICollection<CountryViewModel>>(result);
    }
}

Vanilla approach

public class CountriesController : Controller
{
    private readonly IEntityControllerServices controllerServices;

    public CountriesController(IEntityControllerServices controllerServices)
    {
        this.controllerServices = controllerServices;
    }

    public async Task<IActionResult> Index()
    {
        var typeManager = this.controllerServices.PermissionsHub.GetManager<IPermissionsManager>("/Domain/Country");
        await typeManager.DemandPermissionAsync(EntityPermissions.EntityType.Access);

        IQueryable<Country> query = this.controllerServices.Repository.Query<Country>("Currency")
            .OrderBy(x => x.Name);

        var entityManager = this.controllerServices.PermissionsHub.GetManager<IPermissionsManager<Country>>("/Domain/Country/Entities/{entity:Country}") as IQueryFilteringPermissionsManager<Country>;
        query = entityManager.ApplyQueryFilterAsync(query, EntityPermissions.Entity.Read);

        var entities = await query.ToListAsync();
        var result = entities.Select(x => new CountryViewModel(x)).ToList();
        var model = new DefaultEntityIndexModel<CountryViewModel>
        {
            Items = result
        };

        return this.View(model);
    }
}

Clone this wiki locally