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

时间:2012-12-10 13:30:39

标签: c# generics casting

我有三个处理三个基类的通用存储库:

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();
    }
}

是否有办法让一个存储库根据基类以不同方式过滤实体?看起来像:

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();
    }
}

编辑。静默不是关于如何检查实体是否属于特定类型。当您知道可以通过Deleted或其他属性过滤实体时,真正困难的部分是应用过滤器。由于存在TEntity:Entity限制,因此无法访问Deleted属性。

5 个答案:

答案 0 :(得分:3)

你可以通过强制转换来实现,但我会质疑只执行非泛型功能的泛型方法的用处。

我的意思是,如果没有针对多种类型执行的公共代码,那么在我看来,您可能也会在类特定的派生中实现它。

答案 1 :(得分:2)

你可以,但你不应该。

每个实体类型的单独存储库是正确的方法,因为这样可以将实体特定逻辑封装在该实体的存储库中。如果您尝试创建一个通用存储库,则必须通过大量if检查以大量方法添加/更改逻辑。

如果您想尝试促进一些代码重用,您可以从基本存储库提供功能,并允许特定存储库指定行为:

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;
    }
}

使用这种方法,您可以减少重复代码的数量,但可以提高代码库的可读性和可维护性。

答案 2 :(得分:0)

使用'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...

答案 3 :(得分:0)

您可以使用is

进行检查
if (TEntity is LogicalStorageEntity)
    ...
if (TEntity is ArchiveEntity)
    ...

答案 4 :(得分:0)

我使用is而不是as,但这无所谓。

        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();
        }