C#将类类型保存为预处理器

Posted

技术标签:

【中文标题】C#将类类型保存为预处理器【英文标题】:C# save class type as preprocessor 【发布时间】:2020-03-09 12:22:02 【问题描述】:

在 C# 中是否可以像在 C/C++ 中一样将类类型保存为预处理器指令?

我有多个共享代码的服务。主要区别在于调用正确的DbSet 和使用正确的类。

从以下代码到:

public class TaxService

    readonly DatabaseContext db;

    public TaxService(DatabaseContext database)
    
        db = database;
    

    public async Task<string> DeleteAsync(int? id)
    
        if (await db.Taxes.FindAsync(id) is Tax tax)
        
            string title = tax.Name;

            db.Taxes.Remove(tax);

            try
            
                await db.SaveChangesAsync();

                return title;
            
            catch (Exception ex)
            
                throw;
            
        

        return null;
    

    public async Task<List<Tax>> GetAllAsync()
    
        return await db.Taxes.AsNoTracking().ToListAsync();
    

例如:

public class TaxService<T> where T : Tax

    readonly DatabaseContext db;

    DbSet<Tax> dbSet => db.Tax;

    public TaxService(DatabaseContext database)
    
        db = database;
    

    public async Task<string> DeleteAsync(int? id)
    
        if (await dbSet.FindAsync(id) is T tax)
        
            string title = tax.Name;

            dbSet.Remove(tax);

            try
            
                await db.SaveChangesAsync();

                return title;
            
            catch (Exception ex)
            
                throw;
            
        

        return null;
    

    public async Task<List<T>> GetAllAsync()
    
        return await dbSet.AsNoTracking().OfType<T>().ToListAsync();
    

Ofc 上面的设计存在问题。当我尝试调用TaxService 时,我必须通过泛型类型传递Tax 类=> TaxService&lt;Tax&gt;

同样在GetAllAsync 方法中,我必须使用OfType 方法来避免编译器错误。不可能将List&lt;Tax&gt; 作为List&lt;T&gt; 返回

对设计模式有什么建议吗?谢谢

编辑(对 M. B. 的回答):

public class TaxService : TaxService<Tax>

    public TaxService(DatabaseContext database) : base(database)
    
    


public class TaxService<T> where T : Tax

【问题讨论】:

存储库模式,结合“工作单元”可以解决它。 @MortenBork 感谢您的想法。我研究了一下,如果每个类(T)对于 DeleteAsync 方法都有相同的属性(名称),它对我有用。有时它的标题,有时它的名称.. 我想避免使用反射 嗨。请看一下我作为答案提供的示例...通过添加“表达式”作为谓词,您可以在控制器中定义要删除的内容。 【参考方案1】:

我曾经构建过这样的基本控制器。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;

namespace DataAccessLayer.Controllers

    public class BaseController<TEntity> where TEntity : class, new()
    
        public virtual TEntity Get<TContext>(Expression<Func<TEntity, bool>> predicate, TContext context) where TContext : DbContext
        
            var item = context.Set<TEntity>().FirstOrDefault(predicate);
            return item;
        

        public List<TEntity> GetList<TContext>(Expression<Func<TEntity, bool>> predicate, TContext context) where TContext : DbContext
        
            var item = context.Set<TEntity>().Where(predicate).ToList();
            return item;
        

        public IQueryable<TEntity> GetListQueryable<TContext>(Expression<Func<TEntity, bool>> predicate, TContext context) where TContext : DbContext
        
            var item = context.Set<TEntity>().Where(predicate);
            return item;
        

        public List<TEntity> GetAll<TContext>(TContext context) where TContext : DbContext
        
            var item = context.Set<TEntity>().ToList();
            return item;
        

        public IEnumerable<TEntity> GetAllEnumerable<TContext>(TContext context) where TContext : DbContext
        
            IEnumerable<TEntity> item = context.Set<TEntity>();
            return item;
        


        public virtual TEntity Update<TContext>(TEntity input, Expression<Func<TEntity, bool>> predicate, TContext context) where TContext : DbContext
        
            if (input == null)
                return null;

            var existing = context.Set<TEntity>().FirstOrDefault(predicate);

            if (existing != null) context.Entry(existing).CurrentValues.SetValues(input);

            return existing;
        


        public virtual TEntity Insert<TContext>(TEntity input, TContext context) where TContext : DbContext
        
            context.Set<TEntity>().Add(input);

            return input;
        

    

你可以像这样创建一个控制器来使用它:

public class TaxcController : BaseController<Tax>
    
    

然后只是创建一个实例。这样,您就拥有了一个控制器或存储库,以便在必须“关闭”某些事情时创建您的重载或独特的方法。

public void dostuff()

    TaxController taxController = new TaxController();
    taxController.Insert(item, context);


这在处理数据库时非常有用,它是由醉酒的程序员匆忙构建的,因为遵循相同模式的所有内容都可以使用,而所有不遵循相同模式的内容仍然可以通过控制器使用,无论数据库中存在什么异常,控制器都会负责“乱搞”。

这只是一个 PoC,您不必像这样精确地做。只是为了让你的吵闹。

【讨论】:

看起来不错。但是我想到了像 C++ 中的 #define T Tax 之类的东西。在谷歌上进行研究后,它在 C# 中是不可能的:/ 好吧,您可以在 where 添加一个条件,即您只接受“Tax”类作为 T,或者所有 T 必须属于 Tax 类。喜欢:公共类 TaxController where T : Tax 我编辑了我的第一篇文章。你给了我一个想法。谢谢。

以上是关于C#将类类型保存为预处理器的主要内容,如果未能解决你的问题,请参考以下文章

C#预处理器指令

《CLR via C#》之线程处理——任务调度器

有没有比 C# 中的预处理器指令(#if 等)更好的东西?

C#预处理器命令

《C#零基础入门之百识百例》(九十一)预处理器指令 -- 代码示例

《C#零基础入门之百识百例》(九十一)预处理器指令 -- 代码示例