Resolve DI services in repositories
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)
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
I'm facing the same problem.
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));
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>();