今天在改寫架構的時候,遇到這么個錯誤。當時單從字面意思,看上去錯誤是由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,我們的錯誤消失了,看來最后的推測是對的。
謹以此文,權當拋磚引玉。