今天在改写架构的时候,遇到这么个错误。当时单从字面意思,看上去错误是由join的两个不同的表来源不一致引起的。
其中的videoResult和deerpenList均来自与同一个edmx文件,所以两个表的来源不一致导致了错误的发生,这个猜想是不正确的。
正在左右为难之际,在stackoverflow上面发现了一个主题,正好解决了我的难题。这个问题的回答是这样的:
This can happen if your Context property returns a new instance every time
它的字面意思是:如果你的Context每次访问都返回一个新的实例的话,就会造成这个错误。
回想起我之前的构造:
public interface IContext<T>:IDisposable where T:class { DbContext DbContext{get;} IDbSet DbSet{get;} } public class Context<T>:IContext<T> where T:class { public Context() { DbContext = new DbContext(ConfigurationManager.ConnectionStrings["GDeerGardenEntities"].ConnectionString); DbSet = DbContext.Set<T>(); } public void Dispose() { DbContext.Dispose(); } public DbContext DbContext { get; private set; } public IDbSet<T> DbSet { get; private set; } }
由于每次调用,都会新建一个DbContext,所以导致错误的发生。找到原因所在,就好了,我们只需要利用autofac这个ioc容器就行,使用的时候,从容器拿就行了。
所以打开安装器,输入
install-package autofac.mvc3 -project GDeerParkWeb
然后安装完毕,注入一下:
builder.RegisterGeneric(typeof(Context<>)).As(typeof(IContext<>)).SingleInstance();
本以为这样就没问题了。但是在使用的时候,依然会出现上述的错误。
到底原因在哪里呢? 这次排查的线索都断掉了。
想了好久,最后发现可能是泛型的Context存在问题,为什么呢?
因为在取实例化的时候,按照目前的设计,实例上下文应该是这样取得:
Context<bas_video>, Context<bas_deerpen>.
这样,带来的问题就显而易见了: 这两个上下文会产生两个不同的实例!!!!!!!
为什么会产生两个不同的实例呢? 因为泛型T只是一个占位符,当实例化出来的时候,泛型的上下文当然会拿不同的实例去hold住,这样就会造成在进行join操作的时候,出现开头的错误。
如果真是这样,那么我们把去掉,不就可以了吗?
这次我们的重构如下:
public interface IContext { IDbSet<T> DbSet<T>() where T : class; DbContext DbContext { get; } } public class Context:DbContext,IContext { public Context() : base("GDeerGardenEntities") {} public IDbSet<T> DbSet<T>() where T : class { return base.Set<T>(); } public new DbContext DbContext { get; private set; } }
我们的容器注入如下:
builder.RegisterType<Context>().As<IContext>().SingleInstance();
最后上阵使用,wow,我们的错误消失了,看来最后的推测是对的。
谨以此文,权当抛砖引玉。