asp.net webapi 登录信息放在类中 & 单例模式和工厂模式&EF DbContext上下文对象线程内唯一性

(求回答在评论区)前端传Token上来 然后在filter中查询用户信息   是把用户信息放在一个类中,然后全局都在使用吗? 

比如两个人同时登录请求 用户信息类会覆盖吗 还是各用各的 ?

如果是各用各的 原理是什么呢?

个人理解  就是类 每个用户使用都是New了一遍 所以多人同时赋值类的时候都是New了一遍 各用各的  除了全局静态变量

你的c#代码都是在服务端运行,所以你说查出数据放到对象,也是可以的,但是这个对象的生命周期就在请求范围内,出了这个请求生命周期就结束了,如果是静态的话,就相当于驻留在服务端内存

web端每个请求都可以看成一个线程

每一个请求都是独立的,谁调用,那一刻就是谁的值

原来的asp.net就是在baseController赋值用户的信息 

然后每进一个action就赋值到基类中 

Service中继承基类 就直接可以获取用户的信息了

asp.net Core 有socpe生命周期 Autofac的生命周期是一样的

 注入这个生命周期就能在一个请求里共享数据 比如这条可以共享

DI生命周期

DI的生命周期,根据框架、库的不同,会略有差异。此处,我们就以微软的DI扩展为例,来说下DI中常用的几种生命周期。

首先,我们想象一个这样一个场景。假设我们有寄快递的需求,那么我们会致电快递公司:“我们要寄快递,派一个快递员过来收货”。接着,快递公司会如何做呢?

  1. 一直派遣同一个快递员来收货。
  2. 第一周派遣快递员A、第二周派遣快递员B收货。
  3. 每次都派遣一个新的快递员收货。

这对应到生命周期就是:

  1. 单例(Singleton),单一实例,每次使用都是该实例。
  2. 作用域实例(Scoped),在一个作用域(比如单次请求)内是同一个实例,不同的作用域实例不同。
  3. 瞬时实例(Transient),每次使用都创建新的实例。

快递公司也就是我们在DI中常说的容器(Container)了。

顺便说一下 单例模式和工厂模式&DbContext上下文唯一

工厂模式使用时,返回的对象之间没有关系。单例模式返回始终是同一个对象

 dbcontext最好不要单例和静态,这样会出问题,尤其有status track的情况下  内存大可以字典存起来
相当于全局静态变量了

在一次请求中,即一个线程内,若是用到EF数据上下文对象,就创建一个,这也加是很多人的代码中习惯在使用上下文对象时,习惯将对象建立在using中,也是为了尽早释放上下文对象, 但是如果有一个业务逻辑调用了多个dal层的方法,交互数据库多次,这样效率会低一些,而且在使用EF的情况下,我们通常把SaveChange这个方法提到业务逻辑层(下文中会提到),不保证同一个业务逻辑使用的是同一个上下文对象,事务,工作单元模式将无法实现。而且可能造成数据混乱,每次创建的对象执行相应的数据库操作,与此同时,同一次的请求可能包含对数据的不同操作。其他的EF对象内获得的数据可能已经是“过期”的了。即这个数据已经变动过。这就是脏读。

        为了解决这个问题,关键就是上下文对象的创建问题。

        这里首先想到单例模式,不过在这里,不适合用,原因是使用单例模式,会使EF对象得不到及时的资源释放。想象一下,无数个请求对数据库的访问,DbContext对象容器无数次增加对Model对象的Attach监控,内存就爆了。

        优化就是折中的过程,所以第二种方式考虑保证在线程内对象唯一,对于每一个请求使用同一个上下文。如何保证呢,通过微软ASP机制线程相关的HttpContext对象以及CallContext对象。前面一篇文章中说过,HttpContext机制其实就是依靠CallContext对象实现的。先来看使用CallContext解决这个问题

你可以这样做,在网站Common中添加处理类:

/// <summary>
    /// 用来创建EF上下文对象,且保证线程内唯一。
    /// </summary>
    public class DbContextFactory
    {
        //DbContext在System.Data.Entity;中,不过这里直接只引用这一个不行,还有EF其他的一些NameSpace所以直接添加一个实体模型,所有引用都进来了,然后再把模型删了
        public static DbContext GetDbContext()
        {
            DbContext dbContext = (DbContext)CallContext.GetData("dbContext");
            if (dbContext == null)
            {
                dbContext = new WebEntities();
                CallContext.SetData("dbContext", dbContext);
            }
            return dbContext;
        }
    }

 是不是很像缓存的使用策略。

           仔细思考一阵后发现,上面使用CallContext来存储有什么问题?就是说上面是把上下文对象依赖于一个线程。那么由于线程池的存在,线程在处理完一个请求之后,并没有被销毁,存储在CallContext中的上下文对象也一直存在,如果是下一次拿出这个线程去处理另一个请求,这个上下文对象其实也在不断的膨胀,只不过比全局的膨胀的稍微慢一些。而且,有时候一个线程并不一定是拿去处理请求了,如果是服务器拿去处理其他的业务,那就可能引发一些其他的问题。

        所以,改进一下上面的办法,借鉴一下J2EE的hibernate和mybatis,在DbContextFactory中添加一个remove方法,在业务逻辑层中每次请求使用完上下文之后,就把它从线程中移除。

        解决了,可是这办法实在是。。。那如果我一次请求要调几次业务逻辑呢,还是要创建多次上下文。而且这样手动管理的方式,让人痛苦。相信也是由于这个原因,在spring+hibernate中大家也是更愿意用HibernateTemplate而不是HibernateDaoSupport。

        其实我们还有更好的办法,在HttpContext中有一个Items属性,它也可以用来保存key-value,这就完美了,一次请求正好对应着一个HttpContext,请求结束,它自动释放,EF上下文也就不存在了。把上面代码中的CallContext改为HttpContext.Current.Items,OK。

public static DbContext DbContext()
        {
            DbContext dbContext = HttpContext.Current.Items["dbContext"] as DbContext;
            if (dbContext == null)
            {
                dbContext = new WebEntities();
                HttpContext.Current.Items["dbContext"] =  dbContext;
            }
            return dbContext;
        }

再说SavaChanges这个方法,我们现在可以做到EF上下文创建的优化,那么它对数据库的交互呢?这是我们写了无数次的方法:

public int AddUser(User user)
        {
            context.Add(user);
            return context.SaveChanges();
        }

当我们使用一个业务逻辑复杂的方法中,它可能需要使用到多个dal层对象或者说调用多次dal层的方法,上面的写法,调几次,EF就与数据库交互了几次,效率还是很低。那我们何不把与数据库的交互方法SaveChanges()提到bll层来调用,由bll层方法来调用,一次的业务逻辑,只交互一次,形成一种工作单元模式。
          那么怎么提取,由于我们上下文对象在请求内唯一,那么就再简单不过了。

public class DbSession
{
    public static int SaveChanges()
    {
        return DbContextFactory.GetDbContext().SaveChanges();
    }
}

为什么把这个类名取为DbSession,学习JavaEE的朋友可能马上想到了MyBatis,Hibernate,我们封装了一个对数据库的单元操作,与数据库进行交互,就是一次与数据库的会话。

        另外,我刚接触EF的时候就有这个疑问,EF如果做到事务的处理,用TransactionScope或DbConnection?大可不必,如果我们把SaveChanges()提到业务逻辑层,就组成了一个事务单元,再联想一下spring,为什么会把声明式事务放在Service层而不是Dao层,而且SaveChanges()这个方法其实本身事务的特性,如果保持了上下文对象的唯一性,间接也是完成了事务单元。

单例模式定义:确保一个类仅仅能产生一个实例,并且提供一个全局访问点来获取该实例。 单例模式就是创建一个特殊的类,这个类只能有一个实例的对象

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class danli
    {
        static void Main(string[] args)
        {
            sing Instance = sing.GetInstance();
            sing Instance2 = sing.GetInstance();

            Person.Change();
            Console.WriteLine(Instance2.i);
            Console.ReadKey();
        }
    }

    public class Person
    {
        public static void Change()
        {
            sing Instance = sing.GetInstance();
            Instance.i = 10;
        }
    }


    //建立一个单例特殊类
    class sing
    {
        public int i = 1;
        //private sing() { }
        private static sing _Instance = null;//定义一个单例的实例
        public static sing GetInstance()//获取一个对象
        {
            if (_Instance == null)
                _Instance = new sing();
            return _Instance;
        }
    }

}

最初的时候调用特殊类并且设置i=1,对象实例化后Instance.i=1,经过Change方法之后设置Instance.i=10;

输出Instance2.i结果是10.

若非单例情况下,Instance.i的值应该是不确定的,从这也就可以说明,单例模式就是创建一个特殊的类,这个类只能有一个实例的对象,实际上Instance2=return Instance。

下面来说一下什么是工厂模式:

工厂模式 :是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见。因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑使用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    //电脑抽象类
    public abstract class Computer
    {
        public abstract void print();
    }

    public  class Lenovo:Computer
    {
        public override void print()
        {
            Console.WriteLine("This is a Lenovo Computer");
        }
    }

    public class AUSU:Computer
    {
        public override void print()
        {
            Console.WriteLine("This is a AUSU Computer");
        }
    }
    //选择实例化对象的类型
    public class Make
    {
        public static Computer Create(string Type)
        {
            Computer com = null;
            if(Type=="联想")
            {
                com = new Lenovo();
            }
            else if(Type=="华硕")
            {
                com = new AUSU();
            }
            return com;
        }
    }

       
    class danli
    {
        static void Main(string[] args)
        {
            //运用工厂模式实例化对象
            Computer com = Make.Create("联想");
            com.print();
            Console.ReadKey();
        }
    }


}

这个代码是先定义了一个电脑的抽象类,然后通过选择所要实例化出来的电脑的类型来制作方法Create;这个制作方法就相当与一个工厂,这个工厂只能加工联想和华硕的电脑,当然我们也可以“扩大”工厂的生产类型,从而生产其他类型的电脑,但是这种方法显然有一个弊端,就是当这个工厂坏掉之后,所有在这个工厂里加工的电脑都是不能在加工了,然后解决这个问题呢?

这就需要来抽象化工厂了

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    //电脑抽象类
    public abstract class Computer
    {
        public abstract void print();
    }

    public  class Lenovo:Computer
    {
        public override void print()
        {
            Console.WriteLine("This is a Lenovo Computer");
        }
    }

    public class AUSU:Computer
    {
        public override void print()
        {
            Console.WriteLine("This is a AUSU Computer");
        }
    }
    //工厂抽象
    public abstract class Creater
    {
        public abstract Computer CreateComputer();
    }
    //联想工厂抽象
    public class CreaterLenovo :Creater
    {
        public override Computer CreateComputer()
        {
            return new Lenovo();
        }
    }
    //华硕工厂抽象
    public class CreaterAUSU:Creater
    {
        public override Computer CreateComputer()
        {
            return new AUSU();
        }
    }


    //public class Make
    //{
    //    public static Computer Create(string Type)
    //    {
    //        Computer com = null;
    //        if(Type=="联想")
    //        {
    //            com = new Lenovo();
    //        }
    //        else if(Type=="华硕")
    //        {
    //            com = new AUSU();
    //        }
    //        return com;
    //    }
    //}

       
    class danli
    {
        static void Main(string[] args)
        {
            //利用工厂模式实例化对象
            Creater cr = new CreaterAUSU();//实例化华硕工厂
            Computer com = cr.CreateComputer();//在联想工厂里面实例化华硕电脑这个实例
            com.print();
            Console.ReadKey();
        }
    }


}

抽象化工厂其实和抽象化电脑的思路是一样的,代码也是很简单的,很容易看明白

原文地址:https://www.cnblogs.com/netlock/p/13372583.html