具有ID C#的泛型类 [英] Generic class with Id c#

查看:70
本文介绍了具有ID C#的泛型类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不确定是否可行,但是我已经开始了一个新项目,并且试图清理 DbContext 的工作方式。我正在尝试通过使用泛型来做到这一点。
第一部分很简单。我创建这样的 Repository 类:

I am not sure if this is possible, but I have started a new project and I am trying to clean up the way I work with the DbContext. I am trying to do this by using generics. The first part is easy. I create a Repository class like this:

public class Repository<T> where T : class
{
    protected readonly DbContext _dbContext;
    protected readonly DbSet<T> _dbEntitySet;

    public Repository(InteractiveChoicesContext dbContext)
    {
        _dbContext = dbContext;
        _dbEntitySet = dbContext.Set<T>();
    }

    /// <summary>
    /// Gets all the entities
    /// </summary>
    /// <param name="includes">Option includes for eager loading</param>
    /// <returns></returns>
    public IQueryable<T> List(params string[] includes)
    {
        IQueryable<T> query = _dbEntitySet;
        foreach (var include in includes)
            query = query.Include(include);
        return query;
    }

    /// <summary>
    ///     Creates an entity
    /// </summary>
    /// <param name="model"></param>
    public void Create(T model) => _dbEntitySet.Add(model);

    /// <summary>
    ///     Updates an entity
    /// </summary>
    /// <param name="model"></param>
    public void Update(T model) => _dbEntitySet.Update(model);

    /// <summary>
    /// Removes an entity
    /// </summary>
    /// <param name="model"></param>
    public void Remove(T model) => _dbContext.Entry<T>(model).State = EntityState.Deleted;

    /// <summary>
    /// Saves the database context changes
    /// </summary>
    /// <returns></returns>
    public async Task SaveChangesAsync()
    {
        await _dbContext.SaveChangesAsync();
    }

现在我想创建一个通用的 Service 类可以使用所有CRUD方法。我试图这样创建它:

Now I want to create a generic Service class that can use all the CRUD methods. I tried to create it like this:

public class Service<T> : Service<T, int> where T : class
{
    public Service(InteractiveChoicesContext dbContext) : base(dbContext)
    {
    }
}

public class Service<T, TKey>: Repository<T> where T: class
{
    public Service(InteractiveChoicesContext dbContext) : base(dbContext)
    {
    }
    public virtual async Task<IEnumerable<T>> ListAsync() => await List().ToListAsync();
    public virtual async Task<T> GetAsync(TKey id) => await List().SingleOrDefaultAsync(m => m.Id == id);
    public virtual async Task<T> CreateAsync(T model)
    {
        Create(model);
        await SaveChangesAsync();
        return model;
    }
    public virtual async Task<T> UpdateAsync(T model)
    {
        Update(model);
        await SaveChangesAsync();
        return model;
    }
    public virtual async Task<bool> DeleteAsync(TKey id)
    {
        var model = await GetAsync(id);
        Remove(model);
        await SaveChangesAsync();
        return true;
    }
}

我正在使用 TKey 指定原始类型,但问题出在 GetAsync 方法上。显然,它目前不知道对象是什么,因此将无法编译。我收到此错误:

I am using TKey to specify the primitive type, but the issue is with the GetAsync method. Obviously it doesn't know what the object is at this point, so it will not compile. I get this error:

'T'不包含'Id'的定义,并且没有扩展方法'Id'可以接受类型为'T'的第一个参数找到(您是否缺少using指令或程序集引用?)

'T' does not contain a definition for 'Id' and no extension method 'Id' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?)

现在我知道这是因为我没有说 T 是什么。
查看 IdentityFramework 的处理方式,他们使用 IUser ,我可以做同样的事情,但这意味着我所有的实体都必须实现此接口。
如果我创建了这样的接口:

Now I know this is because I haven't said what T is. Looking at how IdentityFramework handle this, they use IUser, I could do the same, but it would mean ALL my entities would have to implement this interface. If I created an interface like this:

public interface IEntity<out TKey>
{
    TKey Id { get; }
}

然后更新我的所有实体以实现这样的接口:

And then updated all my entities to implement the interface like this:

public class Question : IEntity<int>
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; }
    [Required, MaxLength(100)] public string Text { get; set; }
    public bool MultipleChoice { get; set; }

    public OutcomeGroup Group { get; set; }
    public IEnumerable<Answer> Answers { get; set; }
}

这似乎可行。我想我只有一个问题。
这是最好的方法吗?

That seems to work. I guess I only have one question. Is this the best way to do it?

推荐答案

您可以拥有这样的实体:

you can have your entities like this :

public interface IEntity<out TKey>
{
    TKey Id { get; }
}

public class Question<TPrimaryKey> : IEntity<int>
{
     public TPrimaryKey Id { get; set; }
}

然后您的存储库就这样:

then your repository like this :

public class Repository<TEntity, TPrimaryKey> where TEntity: IEntity<TPrimaryKey>
{
    protected readonly DbContext _dbContext;
    protected readonly DbSet<TEntity> _dbEntitySet;

    public Repository(InteractiveChoicesContext dbContext)
    {
        _dbContext = dbContext;
        _dbEntitySet = dbContext.Set<TEntity>();
    }
}

在我看来,这是最好的方法做到通用。

and in my opinion, this is the best way to do it "Generic".

这篇关于具有ID C#的泛型类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆