简体   繁体   English

C#如何处理这种通用约束?

[英]c# how to deal with this generic constraints?

I have three general repository that handle three base classes: 我有三个用于处理三个基类的常规存储库:

public class Entity
{
    public int Id { get; set; }
}

public class Repository
{
    public TEntity[] GetAll<TEntity>() where TEntity : Entity
    {
        return _context.Set<TEntity>.ToArray();
    }
}

public class ArchiveEntity : Entity
{
    public bool Deleted { get; set; }
}

public class ArchiveRepository
{
    public TEntity[] GetAll<TEntity>() where TEntity : ArchiveEntity
    {
        return _context.Set<TEntity>.Where(x => x.Deleted == false).ToArray();
    }
}

public class LogicalStorageEntity : ArchiveEntity
{
    public int StorageId { get; set; }
}

public class LogicalStorageRepository
{
    public int CurrentStorageId { get; set; }

    public TEntity[] GetAll<TEntity>() where TEntity : LogicalStorageEntity
    {
        return _context.Set<TEntity>
            .Where(x => x.Deleted == false)
            .Where(x => x.StorageId = CurrentStorageId)
            .ToArray();
    }
}

Is there way to have one repository that filters entities differently depending on base class? 有没有办法使一个存储库根据基类对实体进行不同的过滤? Something that looks like: 看起来像这样:

public class Entity
{
    public int Id { get; set; }
}

public class ArchiveEntity : Entity
{
    public bool Deleted { get; set; }
}

public class LogicalStorageEntity : ArchiveEntity
{
    public int StorageId { get; set; }
}

public class UniversalRepository
{
    public TEntity[] GetAll<TEntity>() where TEntity : Entity
    {
        if (typeof(TEntity) is LogicalStorageEntity)
        {
            return _context.Set<TEntity>
                .Where(x => /* how to filter by x.Deleted */)
                .Where(x => /* how to filter by x.StorageId */)
                .ToArray();
        }

        if (typeof(TEntity) is ArchiveEntity)
        {
            return _context.Set<TEntity>
                .Where(x => /* how to filter by x.Deleted */)
                .ToArray();
        }

        return _context.Set<TEntity>.ToArray();
    }
}

Edit. 编辑。 The quiestion is not about how to check if entity is of specific type. 问题不在于如何检查实体是否为特定类型。 The real difficult part is to apply filter when you know that entity can be filtered by Deleted or some other property. 真正困难的部分是当您知道可以通过Deleted属性或其他属性来过滤实体时,应用过滤器。 Since there is limitation TEntity : Entity , you cannot access the Deleted property. 由于存在TEntity:Entity限制,因此您无法访问Deleted属性。

You could do it by casting, but I would question the usefulness of a generic method that only performs non-generic functionality. 您可以通过强制转换来实现,但是我会质疑仅执行非泛型功能的泛型方法的有用性。

What I mean is, if there is no common code that is executed for more than one type, then you might really just as well implement it in class specific derivations in my opinion. 我的意思是,如果没有针对一种以上类型执行的通用代码,那么我认为您真的可以在特定于类的派生中很好地实现它。

You can, but you shouldn't. 可以,但是不可以。

The separate repository per entity type is the correct way to go because that way you encapsulate the entity specific logic in the repository for that entity. 每个实体类型使用单独的存储库是正确的方法,因为这样可以将实体特定的逻辑封装在该实体的存储库中。 If you try and make a universal repository you will have to keep adding to/changing the logic in a huge method with loads of if checks. 如果您尝试建立一个通用存储库,则必须以巨大的方法(不断增加if检查)来添加/更改逻辑。

If you want to try and promote some code re-use, you can however provide the functionality from a base repository and allow the specific repositories to specify the behaviour: 如果要尝试促进某些代码的重用,则可以从基本存储库中提供功能,并允许特定存储库指定行为:

public abstract class Repository<TEntity> where TEntity : Entity
{
    protected virtual Expression<Func<TEntity, bool>> Filter { get { return null; } }

    public TEntity[] GetAll()
    {
        if (this.Filter == null)
        {
            return _context.Set<TEntity>().ToArray();
        }
        else
        {
            return _context.Set<TEntity>().Where(this.Filter).ToArray();
        }
    }
}

public class ArchiveRepository : Repository<Archive>
{
    public ArchiveRepository()
    {
        this.Filter = archive => !archive.IsDeleted;
    }
}

Using this approach, you can reduce the amount of repeated code but increase the readability and maintainability of the code base. 使用这种方法,可以减少重复代码的数量,但可以提高代码库的可读性和可维护性。

Use 'as' casting. 使用'as'强制转换。

LogicalStorageEntity lse = entity as LocalStorageEntity;
if (lse != null)
{
     // we know that the entity is a LogicalStorageEntity 
}

ArchiveEntity ae = entity as ArchiveEntity;
if (ae != null)
{
     // we know that the entity is an ArchiveEntity
}
// etc...

You can check that with is : 您可以检查的is

if (TEntity is LogicalStorageEntity)
    ...
if (TEntity is ArchiveEntity)
    ...

I'd use is over as , but it really doesn't matter. 我会使用isas ,但它其实并不重要。

        if (TEntity is LogicalStorageEntity)
        {
            return _context.Set<TEntity>
                .Where(x => /* filter by x.Deleted */)
                .Where(x => /* filter by x.StorageId */)
                .ToArray();
        }

        if (TEntity is ArchiveEntity)
        {
            return _context.Set<TEntity>
                .Where(x => /* filter by x.Deleted */)
                .ToArray();
        }

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

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