Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve DI services in repositories #214

Open
ABrizmohun opened this issue May 5, 2023 · 4 comments
Open

Resolve DI services in repositories #214

ABrizmohun opened this issue May 5, 2023 · 4 comments
Labels

Comments

@ABrizmohun
Copy link

ABrizmohun commented May 5, 2023

I'm using UIOmatic 5.1.4 with Umbraco 10.4. I have my data in a database separate from the Umbraco DB. I have an Entity Framework project to access data from that database with services registered into DI on startup.

services.AddScoped<IsbnService, IsbnService>();
services.AddScoped<TitleService, TitleService>();

How can I resolve those services in my AbstractUIOMaticRepository?

Example npoco

[TableName("isbn")]
[UIOMatic("isbns" , "ISBNs" , "ISBN" , FolderIcon = "icon-users" , ItemIcon = "icon-user" , RenderType =UIOMatic.Enums.UIOMaticRenderType.List , HideFromTree = true , RepositoryType = typeof(ISBNRepository) ,Order =10)]
public class ISBN
{

    [PrimaryKeyColumn(AutoIncrement = true)]
    public int Id { get; set; }

    [Required]
    [UIOMaticField(Name = "TitleId", Order = 1, View =UIOMatic.Constants.FieldEditors.Label)]
    public int TitleId { get; set; }

    [Required]
    [ISBNUnique(ErrorMessage = "ISBN already exists.")]
    [RegularExpression(@"^(\d{13})?$", ErrorMessage = "Please enter 13 digits")]
    [UIOMaticListViewField]
    [UIOMaticField(Name = "Code", Description = "13 digit ", View = Constants.FieldEditors.DirtyTextfield)]
    public string Isbn { get; set; }

    [UIOMaticField(Name = "BindingType", View = Constants.FieldEditors.DirtyDropdown, Config = "{'typeAlias': 'BindingType', 'valueColumn': 'Id', 'sortColumn': 'Description', 'textTemplate' : '{{Description}} '}")]
    public string BindingType { get; set; }

    [UIOMaticField(Name = "Usage Instruction", View = UIOMatic.Constants.FieldEditors.Rte)]
    public string UsageInstruction { get; set; }

    public override string ToString()
    {
        return Isbn;
    }
}

Example repository

public class ISBNRepository : AbstractUIOMaticRepository<ISBN, int>
{
    private readonly IsbnService _isbnService;
    private readonly TitleService _titleService;
    public ISBNRepository(IsbnService isbnService, TitleService titleService)
    {
        _isbnService = isbnService;
        _titleService = titleService;
    }

    public override IEnumerable<ISBN> GetAll(string sortColumn = "", string sortOrder = "")
    {
        return _isbnService.GetAll().Select(q =>
            MapToEntity(q)); ;
    }

    public override UIOMaticPagedResult<ISBN> GetPaged(int pageNumber, int itemsPerPage, string searchTerm = "", IDictionary<string, string> filters = null, string sortColumn = "", string sortOrder = "")
    {
        UIOMaticPagedResult<ISBN> paged = new UIOMaticPagedResult<ISBN>();

        if (filters.ContainsKey("TitleId")
            && int.TryParse (filters["TitleId"], out int  TitleId))
        {
            DomainTitle title = _titleService.GetById(TitleId);
            paged.Items = title.Isbns.Select(q => MapToEntity(q));
        }
        else
        {
            paged.Items = GetAll();
        }

        int offset = (pageNumber - 1) * itemsPerPage;

        //Search by SearchTerm
        paged.Items = paged.Items.Where(x =>
            {
                if (string.IsNullOrEmpty(searchTerm))
                    return true;
                else
                    return x.Isbn.Contains(searchTerm) || x.Isbn.Contains(searchTerm);
            })

            .Skip(offset)
            .Take(itemsPerPage)
            .ToList();

        int total = paged.Items.Count();
        int totalpages = total / itemsPerPage + 1;

        paged.TotalPages = totalpages;
        paged.CurrentPage = pageNumber;
        paged.ItemsPerPage = itemsPerPage;

        return paged;
    }

    public override ISBN Get(int id)
    {
        var isbn = _isbnService.GetById(id);
        return MapToEntity(isbn);
    }

    public override ISBN Create(ISBN entity)
    {
        DomainIsbn isbn = new DomainIsbn();
        isbn.TitleId = entity.TitleId;
        isbn.BookNumber = entity.Isbn;
        isbn.UsageInstruction = entity.UsageInstruction;

        if (int.TryParse(entity.BindingType, out int bt))
        {
            isbn.BindingTypeId = bt;
        }

        isbn.LastModifiedBy = -1;

        var id = _isbnService.Insert(isbn);
        entity.Id = id;
        return entity;
    }

    public override ISBN Update(ISBN entity)
    {
        DomainIsbn t = _isbnService.GetById(entity.Id);
        t.BookNumber = entity.Isbn;
        t.UsageInstruction = entity.UsageInstruction;

        if (int.TryParse(entity.BindingType, out int bt))
        {
            t.BindingTypeId = bt;
        }
        else
        {
            t.BindingTypeId = null;
        }
        _isbnService.Update(t);
        return entity;
    }

    public override void Delete(int[] ids)
    {
        _isbnService.DeleteByIds(ids);
    }

    public override long GetTotalRecordCount()
    {
        return GetAll().Count();
    }

    private ISBN MapToEntity(DomainIsbn _isbn)
    {
        return new ISBN()
        {
            Id = _isbn.Id,
            Isbn = _isbn.BookNumber,
            TitleId = _isbn.TitleId,
            BindingType = _isbn.BindingTypeId == null ? null : _isbn.BindingTypeId.ToString(),
            UsageInstruction = _isbn.UsageInstruction
        };
    }
}

Currently, I get a Constructor on type System.MissingMethodException: [Redacted].ISBNRepository not found.

Stack Trace:

at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture)
   at System.Activator.CreateInstance(Type type, Object[] args)
   at UIOMatic.UIOMaticHelper.GetRepository(UIOMaticAttribute attr, UIOMaticTypeInfo typeInfo)
   at UIOMatic.Services.NPocoObjectService.GetPaged(Type type, Int32 itemsPerPage, Int32 pageNumber, String sortColumn, String sortOrder, IDictionary`2 filters, String searchTerm)
   at UIOMatic.Web.Controllers.ObjectController.GetPaged(String typeAlias, Int32 itemsPerPage, Int32 pageNumber, String sortColumn, String sortOrder, String filters, String searchTerm)
@Lamborobin
Copy link

I have encountered the same error as you in my Umbraco 10 and 11 solutions. I haven't had the time to test it yet without PetaPoco ORM but could this be helpful? (from the uiomatic docs):
https://timgeyssens.gitbook.io/ui-o-matic/12.advanced#iuiomaticobjectservice

@huzzi
Copy link
Contributor

huzzi commented Sep 6, 2023

I'm facing the same problem.

@JasonEleventeen
Copy link

This should really be resolved by getting the service from the container but this used to work (not sure about newer c# versions) as a workaround

HttpContext.RequestServices.GetService(typeof(ISomeService));

@ABrizmohun
Copy link
Author

I ended up forking this project and injecting IServiceProvider into UIOMaticHelper. Then in GetRepository, I resolve my IUIOMaticRepository from DI if it exists otherwise, it creates a new instance.

namespace UIOMatic
{
    public class UIOMaticHelper : IUIOMaticHelper
    {
        private readonly AppCaches _appCaches;
        private readonly IHostingEnvironment _hostingEnvironment;
        private readonly IScopeProvider _scopeProvider;
        private readonly UIOMaticObjectService _uioMaticObjectService;
        private readonly IServiceProvider _serviceProvider;
        private readonly ILogger<IUIOMaticHelper> _logger;

        public UIOMaticHelper(AppCaches appCaches,
            IHostingEnvironment hostingEnvironment,
            IScopeProvider scopeProvider,
            UIOMaticObjectService uioMaticObjectService,
            IServiceProvider serviceProvider,
            ILogger<IUIOMaticHelper> logger)
        {
            _appCaches = appCaches;
            _hostingEnvironment = hostingEnvironment;
            _scopeProvider = scopeProvider;
            _uioMaticObjectService = uioMaticObjectService;
            _serviceProvider = serviceProvider;
            _logger = logger;
        }


        public  IUIOMaticRepository GetRepository(UIOMaticAttribute attr, UIOMaticTypeInfo typeInfo)
        {
            var existingRepositories = _serviceProvider.GetServices<IUIOMaticRepository>();
            var existingRepository = existingRepositories.FirstOrDefault(x => x.GetType() == attr.RepositoryType);
            if (existingRepository is not null)
            {
                return (IUIOMaticRepository)existingRepository;
            }
            return typeof(DefaultUIOMaticRepository).IsAssignableFrom(attr.RepositoryType)
                ? (IUIOMaticRepository)Activator.CreateInstance(attr.RepositoryType, attr, typeInfo, _scopeProvider, _uioMaticObjectService)
                : (IUIOMaticRepository)Activator.CreateInstance(attr.RepositoryType, _scopeProvider);
        }

Then in my project, I register my IUIOMaticRepository into DI.

builder.Services.AddTransient<IUIOMaticRepository, ISBNRepository>();
builder.Services.AddTransient<IUIOMaticRepository, TitleRepository>();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants