Entity framework 将库从ObjectContext转换为DbContext

Entity framework 将库从ObjectContext转换为DbContext,entity-framework,entity-framework-4.1,objectcontext,dbcontext,Entity Framework,Entity Framework 4.1,Objectcontext,Dbcontext,我有一个库(),它允许我使用实体框架非常轻松地围绕数据访问包装一个外观。它使用ObjectContext,并且就我的目的而言,它的性能已经足够好了 但是现在,我们正在兴奋地首先使用DbContext研究代码,当然,我们希望尽可能多地重用/调整我们现有的工作 使用IObjectContextAdapter转换Facade启用库时一切正常,直到我们尝试使用Facade时,收到以下错误: 类型“Employee”不能用作泛型类型或方法“DbContextManagement.FacadeBase”中的

我有一个库(),它允许我使用实体框架非常轻松地围绕数据访问包装一个外观。它使用ObjectContext,并且就我的目的而言,它的性能已经足够好了

但是现在,我们正在兴奋地首先使用DbContext研究代码,当然,我们希望尽可能多地重用/调整我们现有的工作

使用IObjectContextAdapter转换Facade启用库时一切正常,直到我们尝试使用Facade时,收到以下错误:

类型“Employee”不能用作泛型类型或方法“DbContextManagement.FacadeBase”中的类型参数“tenty”。没有从“Employee”到“System.Data.Objects.DataClasses.EntityObject”的隐式引用转换

MSDN说:

DbContext API不支持EntityObject派生类型,若要使用这些实体类型,必须使用ObjectContext API

这很好,但我如何才能继续完成重构以绕过这一缺陷呢

下面是一些代码(引入了换行符):

FacadeBase.cs

namespace DbContextManagement
{
    using System;
    using System.Collections;
    using System.Configuration;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure;
    using System.Data.Metadata.Edm;
    using System.Data.Objects.DataClasses;
    using System.Linq;
    using System.Reflection;

    public abstract class FacadeBase<TDbContext, TEntity>
        where TDbContext : DbContext, new()
        where TEntity : EntityObject
    {
        protected TDbContext DbContext
        {
            get
            {
                if (DbContextManager == null)
                {
                    this.InstantiateDbContextManager();
                }

                return DbContextManager.GetDbContext<TDbContext>();
            }
        }

        private DbContextManager DbContextManager { get; set; }

        public virtual void Add(TEntity newObject)
        {
            var context = ((IObjectContextAdapter)this.DbContext).ObjectContext;

            string entitySetName;

            if (newObject.EntityKey != null)
            {
                entitySetName = newObject.EntityKey.EntitySetName;
            }
            else
            {
                string entityTypeName = newObject.GetType().Name;

                var container = context.MetadataWorkspace.GetEntityContainer(
                                    context.DefaultContainerName, 
                                    DataSpace.CSpace);

                entitySetName = (from meta in container.BaseEntitySets
                                    where meta.ElementType.Name == 
                                       entityTypeName
                                    select meta.Name).First();
            }

            context.AddObject(entitySetName, newObject);
        }

        public virtual void Delete(TEntity obsoleteObject)
        {
            var context = ((IObjectContextAdapter)this.DbContext).ObjectContext;

            context.DeleteObject(obsoleteObject);
        }

        private void InstantiateDbContextManager()
        {
            var objectContextManagerConfiguration = 
               ConfigurationManager.GetSection("DbContext") as Hashtable;

            if (objectContextManagerConfiguration != null && 
                objectContextManagerConfiguration.ContainsKey("managerType"))
            {
                var managerTypeName = 
                   objectContextManagerConfiguration["managerType"] as string;

                if (string.IsNullOrEmpty(managerTypeName))
                {
                    throw new ConfigurationErrorsException(
                        "The managerType attribute is empty.");
                }

                managerTypeName = managerTypeName.Trim().ToLower();

                try
                {
                    var frameworkAssembly = 
                         Assembly.GetAssembly(typeof(DbContextManager));

                    var managerType = 
                         frameworkAssembly.GetType(managerTypeName, true, true);

                    this.DbContextManager = 
                        Activator.CreateInstance(managerType) as DbContextManager;
                }
                catch (Exception e)
                {
                    throw new ConfigurationErrorsException(
                        "The managerType specified in the 
                            configuration is not valid.", e);
                }
            }
            else
            {
                throw new ConfigurationErrorsException(
    "A Facade.DbContext tag or its managerType attribute
    is missing in the configuration.");
            }
        }
    }
}
namespace Facade
{
    using System.Collections.Generic;
    using System.Linq;
    using DataModel;
    using DataModel.Entities;
    using DbContextManagement;

    public sealed class EmployeeFacade : FacadeBase<FleetContext, Employee>
    {
        public Employee GetById(int? employeeId)
        {
            return employeeId == null
                ? null
                : this.DbContext.Employees.FirstOrDefault(m => m.Id == employeeId);
        }
    }
}
namespace DataModel.Entities
{
    public class Employee
    {
        public int Id { get; set; }
        public string Surname { get; set; }
        public string Forename { get; set; }
        public string EmployeeNumber { get; set; }
    }
}

如果您的实体是从
EntityObject
派生的,并且您要重用的代码依赖于基于
EntityObject
的实体,则它是一个显示阻止器。在实体为POCO(无
EntityObject
parent)之前,不能使用DbContext API

顺便说一句,您可以对
ObjectContext
(和POCOs)使用仅代码映射,而不必使用
DbContext
。你只需要:

  • 为描述映射的每个POCO实体/复杂类型创建基于
    EntityTypeConfiguration
    ComplexTypeConfiguration
    的类
  • 使用
    DbModelBuilder
    收集配置并调用
    Build
    获取
    DbModel
    实例
  • DbModel
    实例上调用
    compiled
    ,以获取
    DbCompiledModel
  • 应用程序生命周期的缓存编译模型
  • 在编译模型上需要新的
    ObjectContext
    实例调用
    CreateObjectContext

请注意,代码映射的功能集非常有限,因此,并非所有您当前在EDMX中拥有的功能都可以通过代码映射来实现。

在上面给出的链接中,我非常感谢原始代码的作者,以下是我的结论。它通过了基本场景,但我还没有尝试过更深入的导航和相关查询。即使有问题,我认为这将是错误修复,而不是显示停止

来吧。下面的代码是可重用的,可用于多个上下文,这意味着您可以转到数据库,并在客户端的两行实际代码中返回一些内容

数据模型(类库项目)

您的EF代码是第一个使用DbContext POCO的项目,等等

DbContextManagement(类库项目)

DbContextManager.cs

namespace DbContextManagement
{
    using System.Data.Entity;

    /// <summary>
    /// Abstract base class for all other DbContextManager classes.
    /// </summary>
    public abstract class DbContextManager
    {
        /// <summary>
        /// Returns a reference to an DbContext instance.
        /// </summary>
        /// <typeparam name="TDbContext">The type of the db context.</typeparam>
        /// <returns>The current DbContext</returns>
        public abstract TDbContext GetDbContext<TDbContext>() 
            where TDbContext : DbContext, new();
    }
}
namespace DbContextManagement
{
    using System.Collections.Generic;
    using System.Data.Entity;
    using System.Linq;

    /// <summary>
    /// Manages multiple db contexts.
    /// </summary>
    public sealed class ScopedDbContextManager : DbContextManager
    {
        /// <summary>
        /// List of Object Contexts.
        /// </summary>
        private List<DbContext> contextList;

        /// <summary>
        /// Returns the DbContext instance that belongs to the current DbContextScope.
        /// If currently no DbContextScope exists, a local instance of an DbContext
        /// class is returned.
        /// </summary>
        /// <typeparam name="TDbContext">The type of the db context.</typeparam>
        /// <returns>Current scoped DbContext.</returns>
        public override TDbContext GetDbContext<TDbContext>()
        {
            var currentDbContext = DbContextScope.GetCurrentDbContext<TDbContext>();

            if (currentDbContext != null)
            {
                return currentDbContext;
            }

            if (this.contextList == null)
            {
                this.contextList = new List<DbContext>();
            }

            currentDbContext = this.contextList.OfType<TDbContext>().FirstOrDefault();

            if (currentDbContext == null)
            {
                currentDbContext = new TDbContext();

                this.contextList.Add(currentDbContext);
            }

            return currentDbContext;
        }
    }
}
namespace DbContextManagement
{
使用System.Data.Entity;
/// 
///所有其他DbContextManager类的抽象基类。
/// 
公共抽象类DbContextManager
{
/// 
///返回对DbContext实例的引用。
/// 
///数据库上下文的类型。
///当前DbContext
公共摘要TDbContext GetDbContext()
其中TDbContext:DbContext,new();
}
}
DbContextScope.cs

namespace DbContextManagement
{
    using System;
    using System.Collections.Generic;
    using System.Data.Common;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure;
    using System.Linq;
    using System.Threading;

    /// <summary>
    /// Defines a scope wherein only one DbContext instance is created, and shared by all of those who use it. 
    /// </summary>
    /// <remarks>Instances of this class are supposed to be used in a using() statement.</remarks>
    public class DbContextScope : IDisposable
    {
        /// <summary>
        /// List of current DbContexts (supports multiple contexts).
        /// </summary>
        private readonly List<DbContext> contextList;

        /// <summary>
        /// DbContext scope definitiion.
        /// </summary>
        [ThreadStatic]
        private static DbContextScope currentScope;

        /// <summary>
        /// Holds a value indicating whether the context is disposed or not.
        /// </summary>
        private bool isDisposed;

        /// <summary>
        /// Initializes a new instance of the <see cref="DbContextScope"/> class.
        /// </summary>
        /// <param name="saveAllChangesAtEndOfScope">if set to <c>true</c> [save all changes at end of scope].</param>
        protected DbContextScope(bool saveAllChangesAtEndOfScope)
        {
            if (currentScope != null && !currentScope.isDisposed)
            {
                throw new InvalidOperationException("DbContextScope instances cannot be nested.");
            }

            this.SaveAllChangesAtEndOfScope = saveAllChangesAtEndOfScope;

            this.contextList = new List<DbContext>();

            this.isDisposed = false;

            Thread.BeginThreadAffinity();

            currentScope = this;
        }

        /// <summary>
        /// Gets or sets a value indicating whether to automatically save all object changes at end of the scope.
        /// </summary>
        /// <value><c>true</c> if [save all changes at end of scope]; otherwise, <c>false</c>.</value>
        private bool SaveAllChangesAtEndOfScope { get; set; }

        /// <summary>
        /// Save all object changes to the underlying datastore.
        /// </summary>
        public void SaveAllChanges()
        {
            var transactions = new List<DbTransaction>();

            foreach (var context in this.contextList
                .Select(dbcontext => ((IObjectContextAdapter)dbcontext)
                    .ObjectContext))
            {
                context.Connection.Open();

                var databaseTransaction = context.Connection.BeginTransaction();

                transactions.Add(databaseTransaction);

                try
                {
                    context.SaveChanges();
                }
                catch
                {
                    /* Rollback & dispose all transactions: */
                    foreach (var transaction in transactions)
                    {
                        try
                        {
                            transaction.Rollback();
                        }
                        catch
                        {
                            // "Empty general catch clause suppresses any errors."
                            // Haven't quite figured out what to do here yet.
                        }
                        finally
                        {
                            databaseTransaction.Dispose();
                        }
                    }

                    transactions.Clear();

                    throw;
                }
            }

            try
            {
                /* Commit all complete transactions: */
                foreach (var completeTransaction in transactions)
                {
                    completeTransaction.Commit();
                }
            }
            finally
            {
                /* Dispose all transactions: */
                foreach (var transaction in transactions)
                {
                    transaction.Dispose();
                }

                transactions.Clear();

                /* Close all open connections: */
                foreach (var context in this.contextList
                    .Select(dbcontext => ((IObjectContextAdapter)dbcontext).ObjectContext)
                    .Where(context => context.Connection.State != System.Data.ConnectionState.Closed))
                {
                    context.Connection.Close();
                }
            }
        }

        /// <summary>
        /// Disposes the DbContext.
        /// </summary>
        public void Dispose()
        {
            // Monitor for possible future bugfix.
            // CA1063 : Microsoft.Design : Provide an overridable implementation of Dispose(bool) 
            // on 'DbContextScope' or mark the type as sealed. A call to Dispose(false) should 
            // only clean up native resources. A call to Dispose(true) should clean up both managed 
            // and native resources.
            if (this.isDisposed)
            {
                return;
            }

            // Monitor for possible future bugfix.
            // CA1063 : Microsoft.Design : Modify 'DbContextScope.Dispose()' so that it calls 
            // Dispose(true), then calls GC.SuppressFinalize on the current object instance 
            // ('this' or 'Me' in Visual Basic), and then returns.
            currentScope = null;

            Thread.EndThreadAffinity();

            try
            {
                if (this.SaveAllChangesAtEndOfScope && this.contextList.Count > 0)
                {
                    this.SaveAllChanges();
                }
            }
            finally
            {
                foreach (var context in this.contextList)
                {
                    try
                    {
                        context.Dispose();
                    }
                    catch (ObjectDisposedException)
                    {
                        // Monitor for possible future bugfix.
                        // CA2202 : Microsoft.Usage : Object 'databaseTransaction' can be disposed 
                        // more than once in method 'DbContextScope.SaveAllChanges()'. 
                        // To avoid generating a System.ObjectDisposedException you should not call 
                        // Dispose more than one time on an object.
                    }
                }

                this.isDisposed = true;
            }
        }

        /// <summary>
        /// Returns a reference to a DbContext of a specific type that is - or will be -
        /// created for the current scope. If no scope currently exists, null is returned.
        /// </summary>
        /// <typeparam name="TDbContext">The type of the db context.</typeparam>
        /// <returns>The current DbContext</returns>
        protected internal static TDbContext GetCurrentDbContext<TDbContext>()
            where TDbContext : DbContext, new()
        {
            if (currentScope == null)
            {
                return null;
            }

            var contextOfType = currentScope.contextList
                .OfType<TDbContext>()
                .FirstOrDefault();

            if (contextOfType == null)
            {
                contextOfType = new TDbContext();

                currentScope.contextList.Add(contextOfType);
            }

            return contextOfType;
        }
    }
}
namespace DbContextManagement
{
使用制度;
使用System.Collections.Generic;
使用System.Data.Common;
使用System.Data.Entity;
使用System.Data.Entity.Infrastructure;
使用System.Linq;
使用系统线程;
/// 
///定义一个范围,其中仅创建一个DbContext实例,并由所有使用它的人共享。
/// 
///此类的实例应该在using()语句中使用。
公共类DbContextScope:IDisposable
{
/// 
///当前数据库上下文列表(支持多个上下文)。
/// 
私有只读列表contextList;
/// 
///DbContext范围定义。
/// 
[线程静态]
私有静态DbContextScope currentScope;
/// 
///保存一个值,该值指示是否释放上下文。
/// 
私人住宅被出售;
/// 
///初始化类的新实例。
/// 
///如果设置为true[在范围结束时保存所有更改]。
受保护的DbContextScope(bool saveAllChangesAtEndOfScope)
{
if(currentScope!=null&&!currentScope.isDisposed)
{
抛出新的InvalidOperationException(“无法嵌套DbContextScope实例”);
}
this.SaveAllChangesAtEndOfScope=SaveAllChangesAtEndOfScope;
this.contextList=新列表();
this.isDisposed=false;
Thread.BeginThreadAffinity();
currentScope=这个;
}
/// 
///获取或设置一个值,该值指示是否在作用域末尾自动保存所有对象更改。
/// 
///如果[在范围末尾保存所有更改],则为true;否则为false。
私有bool SaveAllChangesAtEndOfScope{get;set;}
/// 
///将所有对象更改保存到基础数据存储。
/// 
public void SaveAllChanges()
{
var事务=新列表();
foreach(此.contextList中的变量上下文
.选择(dbcontext=>((IObjectContextAdapter)dbcontext)
.ObjectContext)
{
context.Connection.Open();
var databaseTransaction=context.Connection.BeginTransaction();
事务。添加(数据库事务);
尝试
{
续
namespace DbContextManagement
{
    using System.Collections.Generic;
    using System.Data.Entity;
    using System.Linq;

    /// <summary>
    /// Manages multiple db contexts.
    /// </summary>
    public sealed class ScopedDbContextManager : DbContextManager
    {
        /// <summary>
        /// List of Object Contexts.
        /// </summary>
        private List<DbContext> contextList;

        /// <summary>
        /// Returns the DbContext instance that belongs to the current DbContextScope.
        /// If currently no DbContextScope exists, a local instance of an DbContext
        /// class is returned.
        /// </summary>
        /// <typeparam name="TDbContext">The type of the db context.</typeparam>
        /// <returns>Current scoped DbContext.</returns>
        public override TDbContext GetDbContext<TDbContext>()
        {
            var currentDbContext = DbContextScope.GetCurrentDbContext<TDbContext>();

            if (currentDbContext != null)
            {
                return currentDbContext;
            }

            if (this.contextList == null)
            {
                this.contextList = new List<DbContext>();
            }

            currentDbContext = this.contextList.OfType<TDbContext>().FirstOrDefault();

            if (currentDbContext == null)
            {
                currentDbContext = new TDbContext();

                this.contextList.Add(currentDbContext);
            }

            return currentDbContext;
        }
    }
}
namespace DbContextManagement
{
    /// <summary>
    /// Defines a scope for a business transaction. At the end of the scope all object changes can be persisted to the underlying datastore. 
    /// </summary>
    /// <remarks>Instances of this class are supposed to be used in a using() statement.</remarks>
    public sealed class UnitOfWorkScope : DbContextScope
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="UnitOfWorkScope"/> class.
        /// </summary>
        /// <param name="saveAllChangesAtEndOfScope">if set to <c>true</c> [save all changes at end of scope].</param>
        public UnitOfWorkScope(bool saveAllChangesAtEndOfScope)
            : base(saveAllChangesAtEndOfScope)
        {
        }
    }
}
namespace Facade
{
    using System.Collections.Generic;
    using System.Linq;
    using DataModel;
    using DataModel.Entities;
    using DbContextManagement;

    public class YourEntityFacade : FacadeBase<YourDbContext, YourEntity, int>
    {
        public override IEnumerable<YourEntity> GetAll()
        {
            return base.GetAll()
                .Distinct()
                .ToList();
        }
    }
}
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="DbContext" type="System.Configuration.SingleTagSectionHandler" />
    </configSections>
    <DbContext managerType="DbContextManagement.ScopedDbContextManager" />
    <connectionStrings>
        <add 
            name="YourDbContext" 
            providerName="System.Data.SqlClient" 
            connectionString="Your connection string" />
    </connectionStrings>
</configuration>
namespace TestConsole
{
    using System;
    using System.Collections.Generic;
    using DataModel.Entities;
    using DbContextManagement;
    using Facade;

    public static class Program
    {
        public static void Main()
        {
            TestGetAll();

            Console.ReadLine();
        }

        private static void TestGetAll()
        {
            Console.WriteLine();
            Console.WriteLine("Test GetAll()");
            Console.WriteLine();

            IEnumerable<YourEntity> yourEntities;

            using (new UnitOfWorkScope(false))
            {
                yourEntities= new YourEntityFacade().GetAll();
            }

            if (yourEntities != null)
            {
                foreach (var yourEntity in yourEntities)
                {
                    Console.WriteLine(
                       string.Format("{0}, {1}", 
                       yourEntity.Id, 
                       yourEntity.Name));
                }
            }
            else
            {
                Console.WriteLine("GetAll() NULL");
            }
        }
    }
}