简体   繁体   English

c# 将类型参数作为接口传递 | 禁用延迟加载

[英]c# pass type parameter as interface | disable LazyLoading

Maybe this is already solved somewhere but I can´t find a solution...也许这已经在某个地方解决了,但我找不到解决方案......

I work with EF Core and I want to get rid of lazyLoading.我使用 EF Core,我想摆脱lazyLoading。 I want to write a generic method, which includes different NavigationProperties where needed.我想编写一个通用方法,其中包括需要的不同 NavigationProperties。 I have an Interface which defines crud-methods.我有一个定义 crud 方法的接口。 I use AutoMapper to map the recieved entities to viewModels.我使用 AutoMapper 将接收到的实体映射到视图模型。 I have different groups of entities.我有不同的实体组。 Some can be retrieved without any Navigations, others need specific navigationProperties included.有些可以在没有任何导航的情况下检索,有些则需要包含特定的导航属性。

Actually I want to use one Base-Service-Interface and choose the right implementation during runtime.实际上,我想使用一个 Base-Service-Interface 并在运行时选择正确的实现。 Or some conditional decision (as described below) in the Service-Implementation.或者服务实现中的一些有条件的决定(如下所述)。

This is my Service:这是我的服务:

public interface IService
{
    Task<IEnumerable<TViewModel> GetAsync<TEntity, TViewModel>()
        where TEntity : class
        where TViewModel : class;

    Task PostAsync<TEntity, TViewModel>(TViewModel model)
        where TEntity : class
        where TViewModel : class;

    Task<TViewModel> PatchAsync<TEntity, TViewModel>(int id)
        where TEntity : class;
}

This is my Navigation-Interface:这是我的导航界面:

public interface INavigate
{
    int NavigationTypeId {get;set;}
    NavigationType NavigationType {get;set;}
}

This is my service-implementation:这是我的服务实现:

public class Service
{
    public async Task PatchAsync<TEntity, TViewModel>(Guid id,
JsonPatchDocument<TViewModel> patchDoc)
        where TEntity : class
        where TViewModel : class
    {
        var query = this.dbContext.Set<TEntity>();
        if(typeof(INavigate).IsAssignableFrom(typeof(TEntity)))
        {
            query = this.dbContext.Set<TEntity>()
                .Include(e=>e.NavigationType); // obviously this won't work
        }
        var entity = await query.FirstAsync();
        var viewModel = this.mapper.Map<TViewModel>(entity);
        patchDocument.ApplyTo(viewModel);
        var updatedEntity = this.mapper.Map(viewModel, entity);
        this.dbContext.Entry(entity).CurrentValues.SetValues(updatedEntity);
        this.dbContext.SaveChangesAsync();
    }
}

So... This isn't a solution.所以...这不是解决方案。 I think there must be some way to solve this problem somehow without generating different services for each Navigation-Interface and specific where-clauses ( where TEntity : class, INavigate ) - but I don't know where to go from here.我认为必须有某种方法可以以某种方式解决这个问题,而无需为每个导航接口和特定的 where 子句( where TEntity : class, INavigate )生成不同的服务 - 但我不知道从哪里开始。

If someone else is looking for a solution I'll post the implementation I ended up with.如果其他人正在寻找解决方案,我将发布我最终得到的实现。 Thanks to Svyatoslav Danyliv who pointed me to this direction.感谢 Svyatoslav Danyliv,他为我指明了这个方向。 If someone has a better solution I'd highly appreciate (and accept) an answer.如果有人有更好的解决方案,我会非常感谢(并接受)一个答案。

I use Automapper and have created a bidirectional Map for every entity/viewModel pair.我使用 Automapper 并为每个实体/视图模型对创建了一个双向映射。 I have different groups of entities.我有不同的实体组。 They either have different navigationProperties which are part of the viewModel or navigationProperties which aren't needed during crud or no navigationProperties at all.它们要么具有不同的 navigationProperties,它们是 viewModel 的一部分,要么具有在 crud 期间不需要的 navigationProperties,或者根本没有 navigationProperties。 I use different interfaces for those navigations (such as INavigate).我为这些导航使用不同的界面(例如 INavigate)。 Some navigations have more than one level to include:一些导航有不止一个级别包括:

public interface ITopLevelNavigation
{
    int TopLevelNavigationId { get; set; }
    TopLevelNavigation TopLevelNavigation { get; set; }
}

public class TopLevelNavigation : INavigate
{
    public int Id { get; set; }
    public int NavigationTypeId { get; set; }
    public NavigationType NavigationType { get; set; }
}

And I have entities which have a required relationship:我有具有所需关系的实体:

public interface IRequireDependency
{
    int DependencyId { get; set; }
    RequiredDependency Dependency { get; set; }
}

I want to assure (for insert and update), that these dependencies exist to avoid an ef-core exception (ForeignKey-Violation) and being able to respond with an accurate and understandable feedback.我想保证(对于插入和更新),这些依赖项的存在是为了避免 ef-core 异常(ForeignKey-Violation)并能够以准确且可理解的反馈进行响应。

Based on the comments for my question, the easiest case is Get:根据对我的问题的评论,最简单的情况是 Get:

public Task<IEnumerable<TViewModel> GetAsync<TEntity, TViewModel>()
    where TEntity : class
{
    return await this.mapper.ProjectTo<TViewModel>(this.dbContext.Set<TEntity>())
        .ToListAsync();
}

This works perfectly fine.这工作得很好。 For generic post I only need to validate required dependencies, everything else can be done with Automapper:对于通用帖子,我只需要验证所需的依赖项,其他一切都可以使用 Automapper 完成:

public async Task PostAsync<TEntity, TViewModel>(TViewModel model)
    where TEntity : class
    where TViewModel : class
{
    var entity = this.mapper.Map<TEntity>(model);
    if (entity is IRequireDependency dependency)
    {
        if(!await this.dbContext.RequiredDependency
            .AnyAsync(e => e.Id == dependency.DependencyId)) 
                throw new Exception("Invalid DependencyId");
    }
    await this.dbContext.Set<TEntity>().AddAsync(entity);
    await this.dbContext.SaveChangesAsync();
}

To solve the include-issue I have to use the string-overload.为了解决包含问题,我必须使用字符串重载。 I wrote an extension-method:我写了一个扩展方法:

public static IQueryable<T> IncludeByInterface<T>(this IQueryable<T> queryable)
    where T : class
{
    if (typeof(INavigate).IsAssignableFrom(typeof(T)))
    {
        queryable = queryable.Include(nameof(INavigate.NavigationType));
    }
    if (typeof(ITopLevelNavigation).IsAssignableFrom(typeof(T)))
    {
        queryable = queryable.Include(nameof(ITopLevelNavigation.TopLevelNavigation));
        queryable = queryable.Include($"{nameof(ITopLevelNavigation.TopLevelNavigation)}.
            {nameof(ITopLevelNavigation.Navigation.NavigationType)}");
    }
    return queryable;
}

Now I can patch it like this:现在我可以像这样修补它:

public async Task Patch<TEntity, TViewModel>(int id, JsonPatchDocument<TViewModel> doc)
    where TEntity : class
    where TViewModel : class
{
    var entity = await this.dbContext.Set<TEntity>().AsQueryable()
        .IncludeByInterface().FirstAsync();
    var viewModel = this.mapper.Map<TViewModel>(entity);
    patchDocument.ApplyTo(viewModel);
    var updatedEntity = this.mapper.Map(viewModel, entity);
    this.dbContext.Entry(entity).CurrentValues.SetValues(updatedEntity);
    await this.dbContext.SaveChangesAsync();
}

I hope this helps if someone is facing similar issues.如果有人面临类似问题,我希望这会有所帮助。 And again: If you think this can be done better - please let me know.再说一遍:如果您认为这可以做得更好 - 请告诉我。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM