欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

利用EF6简单实现多租户的应用

程序员文章站 2022-04-29 09:54:32
什么是多租户 网上有好多解释,有些上升到了架构设计,让你觉得似乎非常高深莫测,特别是目前流行的abp架构中就有提到多租户(imusthavetenant),其实说的简单一点就是再...

什么是多租户

网上有好多解释,有些上升到了架构设计,让你觉得似乎非常高深莫测,特别是目前流行的abp架构中就有提到多租户(imusthavetenant),其实说的简单一点就是再每一张数据库的表中添加一个tenantid的字段,用于区分属于不同的租户(或是说不同的用户组)的数据。关键是现实的方式必须对开发人员来说是透明的,不需要关注这个字段的信息,由后台或是封装在基类中实现数据的筛选和更新。

基本原理

从新用户注册时就必须指定用户的tenantid,我的例子是用companyid,公司信息做为tenantid,哪些用户属于不同的公司,每个用户将来只能修改和查询属于本公司的数据。

接下来就是用户登录的时候获取用户信息的时候把tenantid保存起来,asp.net mvc(不是 core) 是通过 identity 2.0实现的认证和授权,这里需要重写部分代码来实现。

最后用户对数据查询/修改/新增时把用户信息中tenantid,这里就需要设定一个filter(过滤器)和每次savechange的插入tenantid

如何实现

第一步,扩展 asp.net identity user 属性,必须新增一个tenantid字段,根据asp.net mvc 自带的项目模板修改identitymodels.cs 这个文件

// you can add profile data for the user by adding more properties to your applicationuser class, please visit http://go.microsoft.com/fwlink/?linkid=317594 to learn more.
 public class applicationuser : identityuser
 {
 public async task<claimsidentity> generateuseridentityasync(usermanager<applicationuser> manager, string authenticationtype)
 {
  // note the authenticationtype must match the one defined in cookieauthenticationoptions.authenticationtype
  var useridentity = await manager.createidentityasync(this, authenticationtype);
  // add custom user claims here
  useridentity.addclaim(new claim("http://schemas.microsoft.com/identity/claims/tenantid", this.tenantid.tostring()));
  useridentity.addclaim(new claim("companyname", this.companyname));
  useridentity.addclaim(new claim("enabledchat", this.enabledchat.tostring()));
  useridentity.addclaim(new claim("fullname", this.fullname));
  useridentity.addclaim(new claim("avatarsx50", this.avatarsx50));
  useridentity.addclaim(new claim("avatarsx120", this.avatarsx120));
  return useridentity;
 }
 public async task<claimsidentity> generateuseridentityasync(usermanager<applicationuser> manager)
 {
  // note the authenticationtype must match the one defined in cookieauthenticationoptions.authenticationtype
  var useridentity = await manager.createidentityasync(this, defaultauthenticationtypes.applicationcookie);
  // add custom user claims here
  return useridentity;
 }

 [display(name = "全名")]
 public string fullname { get; set; }
 [display(name = "性别")]
 public int gender { get; set; }
 public int accounttype { get; set; }
 [display(name = "所属公司")]
 public string companycode { get; set; }
 [display(name = "公司名称")]
 public string companyname { get; set; }
 [display(name = "是否在线")]
 public bool isonline { get; set; }
 [display(name = "是否开启聊天功能")]
 public bool enabledchat { get; set; }
 [display(name = "小头像")]
 public string avatarsx50 { get; set; }
 [display(name = "大头像")]
 public string avatarsx120 { get; set; }
 [display(name = "租户id")]
 public int tenantid { get; set; }
 }



 public class applicationdbcontext : identitydbcontext<applicationuser>
 {
 public applicationdbcontext()
  : base("defaultconnection", throwifv1schema: false) => database.setinitializer<applicationdbcontext>(null);

 public static applicationdbcontext create() => new applicationdbcontext();


 }

第二步 修改注册用户的代码,注册新用户的时候需要选择所属的公司信息

利用EF6简单实现多租户的应用

[httppost]
 [allowanonymous]
 [validateantiforgerytoken]
 public async task<actionresult> register(accountregistrationmodel viewmodel)
 {
  var data = this._companyservice.queryable().select(x => new listitem() { value = x.id.tostring(), text = x.name });
  this.viewbag.companylist = data;

  // ensure we have a valid viewmodel to work with
  if (!this.modelstate.isvalid)
  {
  return this.view(viewmodel);
  }

  // try to create a user with the given identity
  try
  {
  // prepare the identity with the provided information
  var user = new applicationuser
  {
   username = viewmodel.username,
   fullname = viewmodel.lastname + "." + viewmodel.firstname,
   companycode = viewmodel.companycode,
   companyname = viewmodel.companyname,
   tenantid=viewmodel.tenantid,
   email = viewmodel.email,
   accounttype = 0
   
  };
  var result = await this.usermanager.createasync(user, viewmodel.password);

  // if the user could not be created
  if (!result.succeeded)
  {
   // add all errors to the page so they can be used to display what went wrong
   this.adderrors(result);

   return this.view(viewmodel);
  }

  // if the user was able to be created we can sign it in immediately
  // note: consider using the email verification proces
  await this.signinasync(user, true);

  return this.redirecttolocal();
  }
  catch (dbentityvalidationexception ex)
  {
  // add all errors to the page so they can be used to display what went wrong
  this.adderrors(ex);

  return this.view(viewmodel);
  }
 }

accountcontroller.cs

第三步 读取登录用户的tenantid 在用户查询和新增修改时把tenantid插入到表中,这里需要引用

z.entityframework.plus,这个是免费开源的一个类库,功能强大

public storecontext()
  : base("name=defaultconnection") {
  //获取登录用户信息,tenantid
  var claimsidentity = (claimsidentity)httpcontext.current.user.identity;
  var tenantclaim = claimsidentity?.findfirst("http://schemas.microsoft.com/identity/claims/tenantid");
  var tenantid = convert.toint32(tenantclaim?.value);
  //设置当对work对象进行查询时默认添加过滤条件
  queryfiltermanager.filter<work>(q => q.where(x => x.tenantid == tenantid));
  //设置当对order对象进行查询时默认添加过滤条件
  queryfiltermanager.filter<order>(q => q.where(x => x.tenantid == tenantid));
 }

 public override task<int> savechangesasync(cancellationtoken cancellationtoken)
 {
  var currentdatetime = datetime.now;
  var claimsidentity = (claimsidentity)httpcontext.current.user.identity;
  var tenantclaim = claimsidentity?.findfirst("http://schemas.microsoft.com/identity/claims/tenantid");
  var tenantid = convert.toint32(tenantclaim?.value);
  foreach (var auditableentity in this.changetracker.entries<entity>())
  {
  if (auditableentity.state == entitystate.added || auditableentity.state == entitystate.modified)
  {
   //auditableentity.entity.lastmodifieddate = currentdatetime;
   switch (auditableentity.state)
   {
   case entitystate.added:
    auditableentity.property("lastmodifieddate").ismodified = false;
    auditableentity.property("lastmodifiedby").ismodified = false;
    auditableentity.entity.createddate = currentdatetime;
    auditableentity.entity.createdby = claimsidentity.name;
    auditableentity.entity.tenantid = tenantid;
    break;
   case entitystate.modified:
    auditableentity.property("createddate").ismodified = false;
    auditableentity.property("createdby").ismodified = false;
    auditableentity.entity.lastmodifieddate = currentdatetime;
    auditableentity.entity.lastmodifiedby = claimsidentity.name;
    auditableentity.entity.tenantid = tenantid;
    //if (auditableentity.property(p => p.created).ismodified || auditableentity.property(p => p.createdby).ismodified)
    //{
    // throw new dbentityvalidationexception(string.format("attempt to change created audit trails on a modified {0}", auditableentity.entity.gettype().fullname));
    //}
    break;
   }
  }
  }
  return base.savechangesasync(cancellationtoken);
 }

 public override int savechanges()
 {
  var currentdatetime = datetime.now;
  var claimsidentity =(claimsidentity)httpcontext.current.user.identity;
  var tenantclaim = claimsidentity?.findfirst("http://schemas.microsoft.com/identity/claims/tenantid");
  var tenantid = convert.toint32(tenantclaim?.value);
  foreach (var auditableentity in this.changetracker.entries<entity>())
  {
  if (auditableentity.state == entitystate.added || auditableentity.state == entitystate.modified)
  {
   auditableentity.entity.lastmodifieddate = currentdatetime;
   switch (auditableentity.state)
   {
   case entitystate.added:
    auditableentity.property("lastmodifieddate").ismodified = false;
    auditableentity.property("lastmodifiedby").ismodified = false;
    auditableentity.entity.createddate = currentdatetime;
    auditableentity.entity.createdby = claimsidentity.name;
    auditableentity.entity.tenantid = tenantid;
    break;
   case entitystate.modified:
    auditableentity.property("createddate").ismodified = false;
    auditableentity.property("createdby").ismodified = false;
    auditableentity.entity.lastmodifieddate = currentdatetime;
    auditableentity.entity.lastmodifiedby = claimsidentity.name;
    auditableentity.entity.tenantid = tenantid;
    break;
   }
  }
  }
  return base.savechanges();
 }

dbcontext.cs

经过以上3步就实现一个简单的多租户查询数据的功能。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。