前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >efcore使用ShardingCore实现分表分库下的多租户

efcore使用ShardingCore实现分表分库下的多租户

作者头像
落寞的鱼丶
发布于 2022-02-21 12:06:13
发布于 2022-02-21 12:06:13
1.6K00
代码可运行
举报
文章被收录于专栏:afjhahfhahajkafjhahfhahajk
运行总次数:0
代码可运行

介绍

本期主角:ShardingCore 一款ef-core下高性能、轻量级针对分表分库读写分离的解决方案,具有零依赖、零学习成本、零业务代码入侵

dotnet下唯一一款全自动分表,多字段分表框架,拥有高性能,零依赖、零学习成本、零业务代码入侵,并且支持读写分离动态分表分库,同一种路由可以完全自定义的新星组件,通过本框架你不但可以学到很多分片的思想和技巧,并且更能学到Expression的奇思妙用

项目地址

背景

因为之前有小伙伴在使用ShardingCore的时候问过我是否可以利用ShardingCore的分库功能实现多租户呢,我的回答是可以的,但是需要针对分库对象进行路由的编写,相当于我一个项目需要实现多租户所有的表都需要实现分库才可以,那么这个在实际应用中将是不切实际的,所以虽然分库可以用来进行多租户但是一般没人会真的这样操作,那么就没有办法在ShardingCore使用合理的多租户外加分表分库了吗,针对这个问题ShardingCore在新的版本x.4.x.x+中进行了实现

功能

ShardingCorex.4.x.x+版本中具体实现了哪些功能呢

  • 多配置支持,可以针对每个租户或者这个配置进行单独的分表分库读写分离的链接配置
  • 数据库配置,支持多配置下每个配置都可以拥有自己的数据库来进行分表分库读写分离
  • 动态多配置,支持动态添加多配置(目前不支持动态删减多配置,后续会支持如果有需要)

场景

假设我们有这么一个多租户系统,这个系统在我们创建好账号后会分配给我们一个单独的数据库和对应的表信息,之后用户可以利用这个租户配置信息进行操作处理

首先我们创建一个AspNetCore的项目

这边才用的.Net6版本的webapi

添加依赖

这边我们添加了三个包,分别是ShardingCore,Microsoft.EntityFrameworkCore.SqlServer,Pomelo.EntityFrameworkCore.MySql,其中ShardingCore用的是预览版的如果不勾选那么将无法显示出来,为什么我们需要添加额外的两个数据库驱动呢,原因是因为我们需要在不同的租户下实现不同的数据库的配置,比如租户A和我们签订的协议里面有说明系统使用开源数据库,或者希望使用Linux平台那么可以针对租户A进行配置MySql或者PgSql,租户B是资深软粉说需要使用MSSQL那么就可以针对其配置MSSQL.一般情况下我们可能不会出现多数据库的情况但是为了照顾到特殊情况我们这边也针对这种情况进行了支持。

公共用户存储

首先在我还没有创建租户的时候是不存在数据库的所以我的数据自然而然不会存在当前租户下,这边我们采用的是存储到其他数据库中,假设我们使用一个公共的数据库作为用户系统.

创建用户系统

创建系统用户和创建系统用户在数据库内的映射关系

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public class SysUser
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string Password { get; set; }
        public DateTime CreationTime { get; set; }
        public bool IsDeleted { get; set; }
    }
    public class SysUserMap:IEntityTypeConfiguration<SysUser>
    {
        public void Configure(EntityTypeBuilder<SysUser> builder)
        {
            builder.HasKey(o => o.Id);
            builder.Property(o => o.Id).IsRequired().IsUnicode(false).HasMaxLength(50);
            builder.Property(o => o.Name).IsRequired().HasMaxLength(50);
            builder.Property(o => o.Password).IsRequired().IsUnicode(false).HasMaxLength(50);
            builder.HasQueryFilter(o => o.IsDeleted == false);
            builder.ToTable(nameof(SysUser));
        }
    }

创建这个数据库该有的配置信息表,便于后期启动后重建

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public class SysUserTenantConfig
    {
        public string Id { get; set; }
        public string UserId { get; set; }
        /// <summary>
        /// 添加ShardingCore配置的Json包
        /// </summary>
        public string ConfigJson { get; set; }
        public DateTime CreationTime { get; set; }
        public bool IsDeleted { get; set; }
    }
    public class SysUserTenantConfigMap:IEntityTypeConfiguration<SysUserTenantConfig>
    {
        public void Configure(EntityTypeBuilder<SysUserTenantConfig> builder)
        {
            builder.HasKey(o => o.Id);
            builder.Property(o => o.Id).IsRequired().IsUnicode(false).HasMaxLength(50);
            builder.Property(o => o.UserId).IsRequired().IsUnicode(false).HasMaxLength(50);
            builder.Property(o => o.ConfigJson).IsRequired().HasMaxLength(2000);
            builder.HasQueryFilter(o => o.IsDeleted == false);
            builder.ToTable(nameof(SysUserTenantConfig));
        }
    }

创建对应的系统用户存储DbContext

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public class IdentityDbContext:DbContext
    {
        public IdentityDbContext(DbContextOptions<IdentityDbContext> options):base(options)
        {
            
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.ApplyConfiguration(new SysUserMap());
            modelBuilder.ApplyConfiguration(new SysUserTenantConfigMap());
        }
    }

创建一个租户的DbContext

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public class TenantDbContext:AbstractShardingDbContext,IShardingTableDbContext
    {
        public TenantDbContext(DbContextOptions<TenantDbContext> options) : base(options)
        {
        }

        public IRouteTail RouteTail { get; set; }
    }

目前我们先定义好后续进行编写内部的租户代码

创建动态租户参数

动态租户分片配置信息在ShardingCore只需要实现IVirtualDataSourceConfigurationParams<TShardingDbContext>接口,但是这个接口有很多参数需要填写,所以这边框架针对这个接口进行了默认参数的抽象类AbstractVirtualDataSourceConfigurationParams<TShardingDbContext>。 这边我们针对配置参数进行配置采用新建一个配置json的对象

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public class ShardingTenantOptions
    {
        public  string ConfigId { get; set;}
        public  int Priority { get; set;}
        public  string DefaultDataSourceName { get; set;}
        public  string DefaultConnectionString { get; set;}
        public DbTypeEnum DbType { get; set; }
    }

参数里面配置了当前数据库,这边比较简单我们就暂时使用单表分库的模式来实现,目前暂时不对每个租户分库进行演示。之后并且编写SqlServerMySql的配置支持

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public class SqlShardingConfiguration : AbstractVirtualDataSourceConfigurationParams<TenantDbContext>
    {
        private static readonly ILoggerFactory efLogger = LoggerFactory.Create(builder =>
        {
            builder.AddFilter((category, level) => category == DbLoggerCategory.Database.Command.Name && level == LogLevel.Information).AddConsole();
        });
        public override string ConfigId { get; }
        public override int Priority { get; }
        public override string DefaultDataSourceName { get; }
        public override string DefaultConnectionString { get; }
        public override ITableEnsureManager TableEnsureManager { get; }

        private readonly DbTypeEnum _dbType;
        public SqlShardingConfiguration(ShardingTenantOptions options)
        {
            ConfigId = options.ConfigId;
            Priority = options.Priority;
            DefaultDataSourceName = options.DefaultDataSourceName;
            DefaultConnectionString = options.DefaultConnectionString;
            _dbType = options.DbType;
            //用来快速判断是否存在数据库中的表
            if (_dbType == DbTypeEnum.MSSQL)
            {
                TableEnsureManager = new SqlServerTableEnsureManager<TenantDbContext>();
            }
            else if (_dbType == DbTypeEnum.MYSQL)
            {
                TableEnsureManager = new MySqlTableEnsureManager<TenantDbContext>();
            }
            else
            {
                throw new NotImplementedException();
            }
        }
        public override DbContextOptionsBuilder UseDbContextOptionsBuilder(string connectionString,
            DbContextOptionsBuilder dbContextOptionsBuilder)
        {
            switch (_dbType)
            {
                case DbTypeEnum.MSSQL:
                    {
                        dbContextOptionsBuilder.UseSqlServer(connectionString).UseLoggerFactory(efLogger);
                    }
                    break;
                case DbTypeEnum.MYSQL:
                    {
                        dbContextOptionsBuilder.UseMySql(connectionString, new MySqlServerVersion(new Version())).UseLoggerFactory(efLogger);
                    }
                    break;
                default: throw new NotImplementedException();
            }
            return dbContextOptionsBuilder;
        }

        public override DbContextOptionsBuilder UseDbContextOptionsBuilder(DbConnection dbConnection,
            DbContextOptionsBuilder dbContextOptionsBuilder)
        {
            switch (_dbType)
            {
                case DbTypeEnum.MSSQL:
                {
                    dbContextOptionsBuilder.UseSqlServer(dbConnection).UseLoggerFactory(efLogger);
                    }
                    break;
                case DbTypeEnum.MYSQL:
                {
                    dbContextOptionsBuilder.UseMySql(dbConnection, new MySqlServerVersion(new Version())).UseLoggerFactory(efLogger);
                    }
                    break;
                default: throw new NotImplementedException();
            }
            return dbContextOptionsBuilder;
        }
    }

编写用户注册接口

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    [Route("api/[controller]/[action]")]
    [ApiController]
    [AllowAnonymous]
    public class PassportController:ControllerBase
    {
        private readonly IdentityDbContext _identityDbContext;

        public PassportController(IdentityDbContext identityDbContext)
        {
            _identityDbContext = identityDbContext;
        }
        [HttpPost]
        public async Task<IActionResult> Register(RegisterRequest request)
        {
            if (await _identityDbContext.Set<SysUser>().AnyAsync(o => o.Name == request.Name))
                return BadRequest("user not exists");
            var sysUser = new SysUser()
            {
                Id = Guid.NewGuid().ToString("n"),
                Name = request.Name,
                Password = request.Password,
                CreationTime=DateTime.Now
            };
            var shardingTenantOptions = new ShardingTenantOptions()
            {
                ConfigId = sysUser.Id,
                Priority = new Random().Next(1,10),
                DbType = request.DbType,
                DefaultDataSourceName = "ds0",
                DefaultConnectionString = GetDefaultString(request.DbType,sysUser.Id)
            };
            var sysUserTenantConfig = new SysUserTenantConfig()
            {
                Id = Guid.NewGuid().ToString("n"),
                UserId = sysUser.Id,
                CreationTime = DateTime.Now,
                ConfigJson = JsonConvert.SerializeObject(shardingTenantOptions)
            };
            await _identityDbContext.AddAsync(sysUser);
            await _identityDbContext.AddAsync(sysUserTenantConfig);
            await _identityDbContext.SaveChangesAsync();
            //注册完成后进行配置生成
            DynamicShardingHelper.DynamicAppendVirtualDataSourceConfig(new SqlShardingConfiguration(shardingTenantOptions));
            return Ok();
        }
        [HttpPost]
        public async Task<IActionResult> Login(LoginRequest request)
        {
            var sysUser = await _identityDbContext.Set<SysUser>().FirstOrDefaultAsync(o=>o.Name==request.Name&&o.Password==request.Password);
            if (sysUser == null)
                return BadRequest("name or password error");

            //秘钥,就是标头,这里用Hmacsha256算法,需要256bit的密钥
            var securityKey = new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes("123123!@#!@#123123")), SecurityAlgorithms.HmacSha256);
            //Claim,JwtRegisteredClaimNames中预定义了好多种默认的参数名,也可以像下面的Guid一样自己定义键名.
            //ClaimTypes也预定义了好多类型如role、email、name。Role用于赋予权限,不同的角色可以访问不同的接口
            //相当于有效载荷
            var claims = new Claim[] {
                new Claim(JwtRegisteredClaimNames.Iss,"https://localhost:5000"),
                new Claim(JwtRegisteredClaimNames.Aud,"api"),
                new Claim("id",Guid.NewGuid().ToString("n")),
                new Claim("uid",sysUser.Id),
            };
            SecurityToken securityToken = new JwtSecurityToken(
                signingCredentials: securityKey,
                expires: DateTime.Now.AddHours(2),//过期时间
                claims: claims
            );
            var token = new JwtSecurityTokenHandler().WriteToken(securityToken);
            return Ok(token);
        }

        private string GetDefaultString(DbTypeEnum dbType, string userId)
        {
            switch (dbType)
            {
                case DbTypeEnum.MSSQL: return $"Data Source=localhost;Initial Catalog=DB{userId};Integrated Security=True;";
                case DbTypeEnum.MYSQL: return $"server=127.0.0.1;port=3306;database=DB{userId};userid=root;password=L6yBtV6qNENrwBy7;";
                default: throw new NotImplementedException();
            }
        }
    }
    
    public class RegisterRequest
    {
        public string Name { get; set; }
        public string Password { get; set; }
        public DbTypeEnum DbType { get; set; }
    }

    public class LoginRequest
    {
        public string Name { get; set; }
        public string Password { get; set; }
    }

简单来说明一下,这边我们采用的是用户的id作为租户id,将租户id作为数据库配置,来支持多配置模式。到此为止我们的用户系统就已经完成了是不是十分的简单仅仅几段代码,用户这边注册完成后将会创建对应的数据库和对应的表,如果你是分表的那么将会自动创建对应的数据库表等信息。

租户系统

租户系统我们做一个订单的简单演示,使用订单id取模,取模取5来进行分表操作

新增租户系统的订单信息

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public class Order
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public DateTime CreationTime { get; set; }
        public bool IsDeleted { get; set; }
    }
    public class OrderMap:IEntityTypeConfiguration<Order>
    {
        public void Configure(EntityTypeBuilder<Order> builder)
        {
            builder.HasKey(o => o.Id);
            builder.Property(o => o.Id).IsRequired().IsUnicode(false).HasMaxLength(50);
            builder.Property(o => o.Name).IsRequired().HasMaxLength(100);
            builder.HasQueryFilter(o => o.IsDeleted == false);
            builder.ToTable(nameof(Order));
        }
    }

新增订单路由

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class OrderVirtualTableRoute:AbstractSimpleShardingModKeyStringVirtualTableRoute<Order>
{
      public OrderVirtualTableRoute() : base(2, 5)
      {
      }

      public override void Configure(EntityMetadataTableBuilder<Order> builder)
      {
          builder.ShardingProperty(o => o.Id);
      }
}

简单的字符串取模

添加租户中间件

添加租户中间件,在系统中如果使用多配置那么就必须要指定本次创建的dbcontext使用的是哪个配置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public class TenantSelectMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly IVirtualDataSourceManager<TenantDbContext> _virtualDataSourceManager;

        public TenantSelectMiddleware(RequestDelegate next, IVirtualDataSourceManager<TenantDbContext> virtualDataSourceManager)
        {
            _next = next;
            _virtualDataSourceManager = virtualDataSourceManager;
        }

        public async Task Invoke(HttpContext context)
        {

            if (context.Request.Path.ToString().StartsWith("/api/tenant", StringComparison.CurrentCultureIgnoreCase))
            {
                if (!context.User.Identity.IsAuthenticated)
                {
                    await _next(context);
                    return;
                }

                var tenantId = context.User.Claims.FirstOrDefault((o) => o.Type == "uid")?.Value;
                if (string.IsNullOrWhiteSpace(tenantId))
                {
                    await DoUnAuthorized(context, "not found tenant id");
                    return;
                }

                using (_virtualDataSourceManager.CreateScope(tenantId))
                {
                    await _next(context);
                }
            }
            else
            {
                await _next(context);
            }
        }

        private async Task DoUnAuthorized(HttpContext context, string msg)
        {
            context.Response.StatusCode = 403;
            await context.Response.WriteAsync(msg);
        }
    }

该中间件拦截/api/tenant路径下的所有请求并且针对这些请求添加对应的租户信息

配置租户扩展初始化数据

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public static class TenantExtension
    {
        public static void InitTenant(this IServiceProvider serviceProvider)
        {
            using (var scope = serviceProvider.CreateScope())
            {
                var identityDbContext = scope.ServiceProvider.GetRequiredService<IdentityDbContext>();
                identityDbContext.Database.EnsureCreated();
                var sysUserTenantConfigs = identityDbContext.Set<SysUserTenantConfig>().ToList();
                if (sysUserTenantConfigs.Any())
                {
                    foreach (var sysUserTenantConfig in sysUserTenantConfigs)
                    {
                        var shardingTenantOptions = JsonConvert.DeserializeObject<ShardingTenantOptions>(sysUserTenantConfig.ConfigJson);
                        DynamicShardingHelper.DynamicAppendVirtualDataSourceConfig(
                            new SqlShardingConfiguration(shardingTenantOptions));
                    }
                }
            }
        }
    }

这边因为我们针对租户信息进行了初始化而不是硬编码,所以需要一个在启动的时候对租户信息进行动态添加

配置多租户

启动配置Startup

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
builder.Services.AddAuthentication();
#region 用户系统配置

builder.Services.AddDbContext<IdentityDbContext>(o =>
    o.UseSqlServer("Data Source=localhost;Initial Catalog=IdDb;Integrated Security=True;"));
//生成密钥
var keyByteArray = Encoding.ASCII.GetBytes("123123!@#!@#123123");
var signingKey = new SymmetricSecurityKey(keyByteArray);
//认证参数
builder.Services.AddAuthentication("Bearer")
    .AddJwtBearer(o =>
    {
        o.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = signingKey,
            ValidateIssuer = true,
            ValidIssuer = "https://localhost:5000",
            ValidateAudience = true,
            ValidAudience = "api",
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero,
            RequireExpirationTime = true,
        };
    });
#endregion
#region 配置ShardingCore
builder.Services.AddShardingDbContext<TenantDbContext>()
    .AddEntityConfig(op =>
    {
        op.CreateShardingTableOnStart = true;
        op.EnsureCreatedWithOutShardingTable = true;
        op.AddShardingTableRoute<OrderVirtualTableRoute>();
    })
    .AddConfig(op =>
    {
        //默认配置一个
        op.ConfigId = $"test_{Guid.NewGuid():n}";
        op.Priority = 99999;
        op.AddDefaultDataSource("ds0", "Data Source=localhost;Initial Catalog=TestTenantDb;Integrated Security=True;");
        op.UseShardingQuery((conStr, b) =>
        {
            b.UseSqlServer(conStr);
        });
        op.UseShardingTransaction((conn, b) =>
        {
            b.UseSqlServer(conn);
        });
    }).EnsureMultiConfig(ShardingConfigurationStrategyEnum.ThrowIfNull);

#endregion

var app = builder.Build();

// Configure the HTTP request pipeline.
app.Services.GetRequiredService<IShardingBootstrapper>().Start();
//初始化启动配置租户信息
app.Services.InitTenant();
app.UseAuthorization();
app.UseAuthorization();
//在认证后启用租户选择中间件
app.UseMiddleware<TenantSelectMiddleware>();

app.MapControllers();

app.Run();

编写租户操作

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    [Route("api/tenant/[controller]/[action]")]
    [ApiController]
    [Authorize(AuthenticationSchemes = "Bearer")]
    public class TenantController : ControllerBase
    {
        private readonly TenantDbContext _tenantDbContext;

        public TenantController(TenantDbContext tenantDbContext)
        {
            _tenantDbContext = tenantDbContext;
        }
        public async Task<IActionResult> AddOrder()
        {
            var order = new Order()
            {
                Id = Guid.NewGuid().ToString("n"),
                CreationTime = DateTime.Now,
                Name = new Random().Next(1,100)+"_name"
            };
            await _tenantDbContext.AddAsync(order);
            await _tenantDbContext.SaveChangesAsync();
            return Ok(order.Id);
        }
        public async Task<IActionResult> UpdateOrder([FromQuery]string id)
        {
            var order =await _tenantDbContext.Set<Order>().FirstOrDefaultAsync(o=>o.Id==id);
            if (order == null) return BadRequest();
            order.Name = new Random().Next(1, 100) + "_name";
            await _tenantDbContext.SaveChangesAsync();
            return Ok(order.Id);
        }
        public async Task<IActionResult> GetOrders()
        {
            var orders =await _tenantDbContext.Set<Order>().ToListAsync();
            return Ok(orders);
        }
    }

启动项目

这边我们基本上已经配置好我们所需要的之后我们就可以直接启动项目了

这边我们通过接口注册了一个TenantA的用户并且选择了使用MSSQL,这边成就帮我们自动生成好了对应的数据库表结构 接下来我么再注册一个TenantB用户选择MySql

通过截图我们可以看到ShardingCore也是为我们创建好了对应的数据库和对应的表信息

登录租户

首先我们登录

TenantA用户token

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo1MDAwIiwiYXVkIjoiYXBpIiwiaWQiOiJkNGMwZjZiNzI5MzE0M2VlYWM0Yjg3NzUwYzE4MWUzOSIsInVpZCI6ImMxMWRkZjFmNTY0MjQwZjc5YTQzNTEzZGMwNmVjZGMxIiwiZXhwIjoxNjQxODI4ODQ0fQ.zJefwnmcIEZm-kizlN7DhwTRgGxiCg52Esa8QmHiEKY

TenantB用户token

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo1MDAwIiwiYXVkIjoiYXBpIiwiaWQiOiIwNzY4NzUwMmVjYzY0NTMyOGFkNTcwZDRkYjMwNDI3MSIsInVpZCI6ImVkODg4YTc3MzAwYTQ4NjZhYmUyNWY2MTE1NmEwZTQzIiwiZXhwIjoxNjQxODI4ODgxfQ.cL0d010jdXLXNGT8M0wsRMqn3VeIxFnV0keM0H3SPzo

接下来我们分别对两个租户进行交叉处理

AddOrder

租户A插入一个订单,订单Id:aef6905f512a4f72baac5f149ef32d21

TenantB用户也插入一个订单,订单id:450f5dd0e82442eca33dfcf3d57fafa3

两个用户处理

通过日志打印明显能够感觉出来两者是区分了不同的数据库

UpdateOrder

GetOrders

总结

通过上述功能的演示相信很多小伙伴应该已经知道他具体的运作流程了,通过配置多个租户信息,在ShardingCore上实现多配置,动态配置,来保证在多租户模式下的分表分库读写分离依然可以使用,并且拥有跟好的适泛性。 如果你需要开发一个大型程序,领导上来就是分库分表,那么在以前大概率是会花费非常多的精力在处理分片这件事情上,而最终项目是否可以做完并且使用还是一个巨大的问题,但是现在不一样了,毕竟ShardingCore之前并没有一款非常好用的分片组件在.net上,并且拥有非常完美的orm作为支持,基本上重来没有一个框架说多租户模式是可以选择数据库的,之前市面上所有的多租户你只能选择一种数据库,目前.Net在开源的状态下我相信会有越来越好的组件框架诞生,毕竟这么好的语言如果配上丰富的生态那将是所有.Neter的福音。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
.NET EF Core(Entity Framework Core)
1、Entity Framework Core(EF Core)是微软官方的ORM框架。优点:功能强大、官方支持、生产效率高、力求屏蔽底层数据库差异;缺点:复杂、上手门槛高、不熟悉EFCore的话可能会进坑。 2、Dapper。优点:简单,N分钟即可上手,行为可预期性强;缺点:生产效率低,需要处理底层数据库差异。 3、EF Core是 模型驱动 (Model-Driven)的开发思想,Dapper是 数据库驱动(DataBase-Driven)的开发思想的。没有优劣,只有比较。 4、性能: Dapper等≠性能高;EF Core≠性能差。 5、EF Core是官方推荐、推进的框架,尽量屏蔽底层数据库差异,.NET开发者必须熟悉,根据的项目情况再决定用哪个。
鱼找水需要时间
2024/03/23
5280
.NET EF Core(Entity Framework Core)
登录注册的小项目对比.Net Core与 .Net Framework的一些区别
UserSys.IServices:主要有实体和对实体的配置,还有对实体的操作接口
用户9184480
2024/12/19
520
登录注册的小项目对比.Net Core与 .Net Framework的一些区别
Magicodes.WeiChat——多租户的设计与实现
多租户(Multi Tenancy/Tenant)是一种软件架构,其定义是:在一台服务器上运行单个应用实例,它为多个租户提供服务。
雪雁-心莱科技
2018/12/27
1.8K0
使用EF6简实现多租户的应用
网上有好多解释,有些上升到了架构设计,让你觉得似乎非常高深莫测,特别是目前流行的ABP架构中就有提到多租户(IMustHaveTenant),其实说的简单一点就是再每一张数据库的表中添加一个TenantId的字段,用于区分属于不同的租户(或是说不同的用户组)的数据。关键是现实的方式必须对开发人员来说是透明的,不需要关注这个字段的信息,由后台或是封装在基类中实现数据的筛选和更新。
阿新
2019/09/10
1K0
使用EF6简实现多租户的应用
一款EF Core下高性能、轻量级针对分表分库读写分离的解决方案
今天大姚给大家分享一款EF Core下高性能、轻量级针对分表分库读写分离的解决方案,开源(Apache License)的EF Core拓展程序包:ShardingCore。
追逐时光者
2024/07/06
1970
一款EF Core下高性能、轻量级针对分表分库读写分离的解决方案
Entity Framework——读写分离
1 实现 CustomDbContext扩展了DbContext,其构造函数带有形式参nameOrConnectionString,可以在使用CustomDbContext时指定数据库连接字符串。 DbContextFactory包含两个属性MasterDbContext和SlaveDbContext,MasterDbContext为主库上下文,SlaveDbContext为从库上下文。DbContextFactory还包含了一个方法:UpdateSlaves用于实现对SlaveDbContext的更新,因
甜橙很酸
2018/03/08
1.1K0
造轮子之菜单管理
前面完成了基础管理的相关API,接下来就得做一个菜单管理了,用于对接管理后台前端界面。
饭勺oO
2023/10/18
2240
造轮子之文件管理
前面我们完成了设置管理,接下来正好配合设置管理来实现文件管理功能。 文件管理自然包括文件上传,下载以及文件存储功能。设计要求可以支持扩展多种存储服务,如本地文件,云存储等等。
饭勺oO
2023/10/25
3180
造轮子之文件管理
基于efcore的分表组件开源
ShardingCore 是一个支持efcore 2.x 3.x 5.x的一个对于数据库分表的一个简易扩展,当然也支持不分表的普通使用,.Net下并没有类似mycat或者sharding-jdbc之类的开源组件或者说有但是并没有非常适用的或者说个人在用过后有一些地方因为限制没法很好使用所以决定自己开发这个库,目前该库暂未支持分库(未来会支持),仅支持分表,该项目的理念是让你可以已最少的代码量来实现自动分表的实现,经过多个开源项目的摸索参考目前正式开源本项目 项目地址 github 喜欢的朋友可以点下star Thanks♪(・ω・)ノ
呆呆
2021/10/09
8090
.NET Core开发实战(第29课:定义仓储:使用EF Core实现仓储层)--学习笔记
映射关系,针对每一个领域模型创建一个 EntityTypeConfiguration
郑子铭
2021/01/13
2.4K0
.NET Core开发实战(第29课:定义仓储:使用EF Core实现仓储层)--学习笔记
efcore分表分库原理解析
ShardingCore 易用、简单、高性能、普适性,是一款扩展针对efcore生态下的分表分库的扩展解决方案,支持efcore2+的所有版本,支持efcore2+的所有数据库、支持自定义路由、动态路由、高性能分页、读写分离的一款组件,如果你喜欢这组件或者这个组件对你有帮助请点击下发star让更多的.neter可以看到使用
呆呆
2021/10/09
1.2K0
一文看懂.NET ORM 分表分库!
分表 - 从表面意思上看呢,就是把一张表分成N多个小表,每一个小表都是完正的一张表。分表后数据都是存放在分表里,总表只是一个外壳,存取数据发生在一个一个的分表里面。分表后单表的并发能力提高了,磁盘I/O性能也提高了。并发能力为什么提高了呢,因为查寻一次所花的时间变短了,如果出现高并发的话,总表可以根据不同 的查询,将并发压力分到不同的小表里面。
用户9161834
2021/11/07
1.3K0
EF Core关系配置
关系配置: EF Core中实体之间关系的配置的套路: HasXXX(…).WithXXX(…); 有XXX、反之带有XXX。 XXX可选值One、Many。
鱼找水需要时间
2024/03/24
1360
EF Core关系配置
使用mycat实现分库分表
分库的原则:有紧密关联关系的表应该在一个库里,相互没有关联的表可以分配到不同的库里。
不凡
2021/11/01
1.1K0
.NET 分库分表高性能:瀑布流分页
依照惯例首先介绍本期主角:ShardingCore 一款ef-core下高性能、轻量级针对分表分库读写分离的解决方案,具有零依赖、零学习成本、零业务代码入侵
郑子铭
2022/03/22
4530
.NET 分库分表高性能:瀑布流分页
【愚公系列】2023年01月 .NET CORE工具案例-基于SqlSugar的多库多表融合查询
SqlSugar 是一款 老牌 .NET 开源ORM框架,由果糖大数据科技团队维护和更新 ,开箱即用,最易上手的ORM框架 ,51Job和Boss直招简历数超过国外框架 Nhibernate PetaPoco, 仅次于Dapper和EF Core , 占Dapper 40% 。
愚公搬代码
2023/03/16
1.2K0
【愚公系列】2023年01月 .NET CORE工具案例-基于SqlSugar的多库多表融合查询
“ShardingCore”是如何针对分表下的分页进行优化的
首先还是要给自己的开原框架打个广告 sharding-core 针对efcore 2+版本的分表组件,首先我们来快速回顾下目前市面上分表下针对分页常见的集中解决方案
呆呆
2021/10/09
8740
mycat 读写分离+分库分表+全局表
设置有两种,如下: (1) 设置balance="1"与writeType="0"
小手冰凉
2020/06/22
9520
C# 动态创建类,动态创建表,支持多库的数据库维护方案
SqlSugar支持了3种模式的建表(无实体建表、实体建表,实体特性建表),非常的灵活
郑子铭
2023/12/13
6080
C# 动态创建类,动态创建表,支持多库的数据库维护方案
用EFCore的 FluentAPI 方式生成MySql 带注释的数据库表结构
4. 创建实体模型 OperateLog , 及实体映射数据库表的 OperateLogConfig
明志德道
2023/10/21
3530
用EFCore的 FluentAPI 方式生成MySql 带注释的数据库表结构
推荐阅读
相关推荐
.NET EF Core(Entity Framework Core)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文