【问题标题】:Database not updated using repository pattern and EF Code First未使用存储库模式和 EF Code First 更新数据库
【发布时间】:2013-07-08 17:26:50
【问题描述】:

我的 web 应用程序有一个分层设计,具有通用服务和存储库构造。我想先使用代码,这样我就可以对我的实体进行编码,然后创建/更新我的数据库。但是我似乎无法让它工作。我是生成数据库并为其播种的代码优先概念的新手,因此它很可能是显而易见的;-)

我的应用程序设计如下。

  • 网站
  • 网站.DAL
  • Website.TESTS(尚未使用)

website.DAL 项目包含我的通用服务和存储库,以及 DataContext 和我的实体。这个想法是我可以在某个实体的控制器内实例化一个泛型服务。该服务可以包含更多功能来进行计算等。存储库仅用于处理 CRUD 操作。网站项目当然参考了 Website.DAL 项目,并且通过 NuGet 在两个项目中安装了 EF5。

DataContext 如下所示:

using System.Data.Entity;
using System.Web;
using Website.DAL.Entities;

namespace Website.DAL.Model
{
    public class MyContext : DbContext
    {
        public IDbSet<Project> Projects { get; set; }
        public IDbSet<Portfolio> Portfolios { get; set; }

        /// <summary>
        /// The constructor, we provide the connectionstring to be used to it's base class.
        /// </summary>
        public MyContext()
            : base("MyConnectionstringName")
        {
            //MyContext.Database.Initialize(true);

            //if (HttpContext.Current.IsDebuggingEnabled)
            //{
            //    //Database.SetInitializer<MyContext>(new DatabaseInitializer());
            //    Database.SetInitializer<MyContext>(null);
            //}
            //else
            //{
            //    //Database.SetInitializer<MyContext>(new CreateInitializer());
            //}
        }

        static MyContext()
        {
            Database.SetInitializer<MyContext>(null);
        }

        /// <summary>
        /// This method prevents the plurarization of table names
        /// </summary>
        /// <param name="modelBuilder"></param>
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Conventions.Remove<System.Data.Entity.ModelConfiguration.Conventions.PluralizingTableNameConvention>();
        }

        //public void Seed(MyContextContext)
        //{
        //    // Normal seeding goes here

        //    Context.SaveChanges();
        //}
    }
}

我还创建了一个当前为空的 DatabaseInitialiser 类,但我的想法当然是在创建或更新数据库时让它成为我的数据库的种子。

DatabaseInitialiser 类如下所示:

using System.Data.Entity;
using Website.DAL.Model;

namespace Website.DAL
{
    public class DatabaseInitializer : DropCreateDatabaseIfModelChanges<MyContext>
    {
        public DatabaseInitializer()
        {

        }             

        protected override void Seed(MyContextcontext)
        {
            //TODO: Implement code to seed database

            //Save all changes
            context.SaveChanges();
        }
    }
}

由于 GenericService 与问题无关,我将其省略,因为它目前仅直接调用存储库而没有任何特定的商业智能。

使用的通用存储库如下所示。这里的事情还需要改进,但现在可以了。

GenericRepository

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Website.DAL.Model;
using Website.DAL.RepositoryInterfaces;

namespace Website.DAL.Repositories
{

    public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
    {
        #region Implementation of IRepository<TEntity>

        //protected SceObjectContext DataContext;
        //protected ObjectContext DataContext;
        private MyContext _context;
        //private IObjectSet<T> ObjectSet;
        private DbSet<TEntity> _dbSet;

        public GenericRepository()
        {
            //DataContext = SceObjectContext.Current;
            //DataContext = new ObjectContext("dbConnection");
            _context = new MyContext();
            //ObjectSet = DataContext.CreateObjectSet<T>();
            _dbSet = _context.Set<TEntity>();
        }

        /// <summary>
        /// Inserts a new object into the database
        /// </summary>
        /// <param name="entity">The entity to insert</param>
        public void Insert(TEntity entity)
        {
            //var entitySetName = GetEntitySetName(typeof(T));
            //DataContext.AddObject(entitySetName, entity);
            _dbSet.Add(entity);
        }

        /// <summary>
        /// Deletes the specified entity from the database
        /// </summary>
        /// <param name="entity">The object to delete</param>
        public void Delete(TEntity entity)
        {
            //DataContext.DeleteObject(entity);
            if (_context.Entry(entity).State == System.Data.EntityState.Detached)
            {
                _dbSet.Attach(entity);
            }

            _dbSet.Remove(entity);
        }

        /// <summary>
        /// Saves all pending chances to the database
        /// </summary>
        public void Save()
        {
            _context.SaveChanges();
        }

        /// <summary>
        /// Retrieves the first object matching the specified query.
        /// </summary>
        /// <param name="where">The where condition to use</param>
        /// <returns>The first matching object, null of none found</returns>
        public TEntity First(Expression<Func<TEntity, bool>> @where)
        {
            return _dbSet.FirstOrDefault(where);
        }

        /// <summary>
        /// Gets a list of all objects
        /// </summary>
        /// <returns>An strong typed list of objects</returns>
        public IEnumerable<TEntity> GetAll()
        {
            return _dbSet.AsEnumerable<TEntity>();
        }

        /// <summary>
        /// Returns ans iQueryable of the matching type
        /// </summary>
        /// <returns>iQueryable</returns>
        public IQueryable<TEntity> AsQueryable()
        {
            return _dbSet.AsQueryable();
        }
        #endregion
    }
}

我已经创建了两个实体。投资组合是其中之一,显示在下方。 Project 是第二个,它只是一个带有 Id 和一些属性的简单 POCO 类。

Portfolio.cs

public class Portfolio
{
    [Key]
    public Guid Id { get; set; }
    public String Name { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime? EndDate { get; set; }
    public bool IsPublished { get; set; }

    public virtual ICollection<Project> Projects { get; set; }
}

以上所有类都在我的 Website.DAL 项目中维护。我的网站项目中的 Global.asax 包含一些调用初始化程序的代码,据我所知,它应该确保可以在不久的将来完成播种并维护数据库表。

Global.asax

try
{
    //Regenerate database if needed.
    //Database.SetInitializer<MyContext>(new DropCreateDatabaseIfModelChanges<MyContext>());
    //Database.SetInitializer(new DatabaseInitializer());
    Database.SetInitializer(new DropCreateDatabaseIfModelChanges<BorloContext>());
    //Database.SetInitializer<MyContext>(new MigrateDatabaseToLatestVersion<MyContext>());
}
catch (Exception)
{
    throw;
}

为此,我在 HomeController 中有一段代码,它应该获取所有投资组合项目的 id。

var list = _portfolioService.GetAll();

在通过代码调试的时候会出现以下情况;

  • Global.asax 通行证中的初始化程序代码。
  • databaseinitialiser 的构造函数被调用
  • homecontroller 中的代码不会引发异常。但是,当向“_portfolioService.GetAll();”的调用添加手表时我收到以下异常;

我无法弄清楚这里出了什么问题。当然异常不好,但我无法查看内部异常,因为它没有给我一个。我能做些什么来让它工作?或者是不是我想要实现的事情是不可能的,是否应该将 DAL 层合并到网站中以使代码生成工作?

更新 1:

好的,我在上下文中更改了以下行

Database.SetInitializer<MyContext>(null);

Database.SetInitializer<MyContext>(new DatabaseInitializer());

现在我在调试“_portfolioService.GetAll();”时遇到此错误和堆栈跟踪调用homecontroller

错误:

无法检查模型兼容性,因为数据库没有 包含模型元数据。只能检查模型兼容性 使用 Code First 或 Code First 迁移创建的数据库。

bij System.Data.Entity.Internal.ModelCompatibilityChecker.CompatibleWithModel(InternalContext internalContext, ModelHashCalculator modelHashCalculator, Boolean throwIfNoMetadata)
   bij System.Data.Entity.Internal.InternalContext.CompatibleWithModel(Boolean throwIfNoMetadata)
   bij System.Data.Entity.Database.CompatibleWithModel(Boolean throwIfNoMetadata)
   bij System.Data.Entity.DropCreateDatabaseIfModelChanges`1.InitializeDatabase(TContext context)
   bij System.Data.Entity.Database.<>c__DisplayClass2`1.<SetInitializerInternal>b__0(DbContext c)
   bij System.Data.Entity.Internal.InternalContext.<>c__DisplayClass8.<PerformDatabaseInitialization>b__6()
   bij System.Data.Entity.Internal.InternalContext.PerformInitializationAction(Action action)
   bij System.Data.Entity.Internal.InternalContext.PerformDatabaseInitialization()
   bij System.Data.Entity.Internal.LazyInternalContext.<InitializeDatabase>b__4(InternalContext c)
   bij System.Data.Entity.Internal.RetryAction`1.PerformAction(TInput input)
   bij System.Data.Entity.Internal.LazyInternalContext.InitializeDatabaseAction(Action`1 action)
   bij System.Data.Entity.Internal.LazyInternalContext.InitializeDatabase()
   bij System.Data.Entity.Internal.InternalContext.Initialize()
   bij System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)
   bij System.Data.Entity.Internal.Linq.InternalSet`1.Initialize()
   bij System.Data.Entity.Internal.Linq.InternalSet`1.GetEnumerator()
   bij System.Data.Entity.Infrastructure.DbQuery`1.System.Collections.Generic.IEnumerable<TResult>.GetEnumerator()
   bij System.Linq.SystemCore_EnumerableDebugView`1.get_Items()

【问题讨论】:

  • 哈! “执行命令定义时发生错误......”你很难回答你的问题,至少对于非荷兰语的人来说不是:) 我必须输入来自的荷兰语消息你的截图到谷歌翻译。消息中有什么有趣的东西吗?它在屏幕截图中被切断...
  • @Slauma,对不起。将翻译错误并更新问题:)
  • @Slauma,更新 1 后见 cmets :)
  • 我实际上是指您的“更新 1”之前的错误。对于您更新中的错误:这意味着尚未使用代码优先创建数据库(或未成功创建,可能是由于第一个错误)。您可以手动删除数据库(例如在 sql server management studio 中)然后再次运行吗?然后初始化程序应该可以工作了。
  • 我建议您只提出一个新问题,即为什么代码优先没有创建数据库。如果您使用 DropCreateDatabaseIfModelChanges 初始化程序并且 DB 不存在 EF 必须 创建 DB。您的设置有问题(可能是连接字符串或访问权限)。很明显,当您手动创建数据库时,您会遇到同样的错误,因为它不包含初始化程序将插入的元数据。

标签: asp.net-mvc entity-framework asp.net-mvc-4 ef-code-first entity-framework-migrations


【解决方案1】:

由于没有其他解决方案,我决定改变我的方法。

我首先自己创建了数据库,并确保配置了正确的 SQL 用户并且我可以访问。

然后我从 Global.asax 文件中删除了初始化程序和代码。之后我在包管理器控制台中运行了以下命令(因为分层设计我必须在控制台中选择正确的项目);

Enable-Migrations

在启用迁移并且我在最后一刻对我的实体进行了一些更改后,我运行了以下命令来构建新的迁移;

Add-Migration AddSortOrder

创建迁移后,我在控制台中运行了以下命令,瞧,数据库已更新为我的实体;

Update-Database -Verbose

为了能够在运行迁移时为数据库播种,我覆盖了我的 Configuraton.cs 类中的 Seed 方法,该方法是在启用迁移时创建的。这个方法中最终的代码是这样的;

protected override void Seed(MyContext context)
        {
            //  This method will be called after migrating to the latest version.

            //Add menu items and pages
            if (!context.Menu.Any() && !context.Page.Any())
            {
                context.Menu.AddOrUpdate(new Menu()
                                             {
                                                 Id = Guid.NewGuid(),
                                                 Name = "MainMenu",
                                                 Description = "Some menu",
                                                 IsDeleted = false,
                                                 IsPublished = true,
                                                 PublishStart = DateTime.Now,
                                                 LastModified = DateTime.Now,
                                                 PublishEnd = null,
                                                 MenuItems = new List<MenuItem>()
                                                                 {
                                                                     new MenuItem()
                                                                         {
                                                                             Id = Guid.NewGuid(),
                                                                             IsDeleted = false,
                                                                             IsPublished = true,
                                                                             PublishStart = DateTime.Now,
                                                                             LastModified = DateTime.Now,
                                                                             PublishEnd = null,
                                                                             Name = "Some menuitem",
                                                                             Page = new Page()
                                                                                        {
                                                                                            Id = Guid.NewGuid(),
                                                                                            ActionName = "Some Action",
                                                                                            ControllerName = "SomeController",
                                                                                            IsPublished = true,
                                                                                            IsDeleted = false,
                                                                                            PublishStart = DateTime.Now,
                                                                                            LastModified = DateTime.Now,
                                                                                            PublishEnd = null,
                                                                                            Title = "Some Page"
                                                                                        }
                                                                         },
                                                                     new MenuItem()
                                                                         {
                                                                             Id = Guid.NewGuid(),
                                                                             IsDeleted = false,
                                                                             IsPublished = true,
                                                                             PublishStart = DateTime.Now,
                                                                             LastModified = DateTime.Now,
                                                                             PublishEnd = null,
                                                                             Name = "Some MenuItem",
                                                                             Page = new Page()
                                                                                        {
                                                                                            Id = Guid.NewGuid(),
                                                                                            ActionName = "Some Action",
                                                                                            ControllerName = "SomeController",
                                                                                            IsPublished = true,
                                                                                            IsDeleted = false,
                                                                                            PublishStart = DateTime.Now,
                                                                                            LastModified = DateTime.Now,
                                                                                            PublishEnd = null,
                                                                                            Title = "Some Page"
                                                                                        }
                                                                         }
                                                                 }
                                             });
            }

            if (!context.ComponentType.Any())
            {
                context.ComponentType.AddOrUpdate(new ComponentType()
                {
                    Id = Guid.NewGuid(),
                    IsDeleted = false,
                    IsPublished = true,
                    LastModified = DateTime.Now,
                    Name = "MyComponent",
                    PublishEnd = null,
                    PublishStart = DateTime.Now
                });
            }


            try
            {
                // Your code...
                // Could also be before try if you know the exception occurs in SaveChanges

                context.SaveChanges();
            }
            catch (DbEntityValidationException e)
            {
                //foreach (var eve in e.EntityValidationErrors)
                //{
                //    Console.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
                //        eve.Entry.Entity.GetType().Name, eve.Entry.State);
                //    foreach (var ve in eve.ValidationErrors)
                //    {
                //        Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"",
                //            ve.PropertyName, ve.ErrorMessage);
                //    }
                //}
                //throw;

                var outputLines = new List<string>();
                foreach (var eve in e.EntityValidationErrors)
                {
                    outputLines.Add(string.Format(
                        "{0}: Entity of type \"{1}\" in state \"{2}\" has the following validation errors:",
                        DateTime.Now, eve.Entry.Entity.GetType().Name, eve.Entry.State));
                    foreach (var ve in eve.ValidationErrors)
                    {
                        outputLines.Add(string.Format(
                            "- Property: \"{0}\", Error: \"{1}\"",
                            ve.PropertyName, ve.ErrorMessage));
                    }
                }
                System.IO.File.AppendAllLines(@"c:\temp\errors.txt", outputLines);
                throw;
            }
        }

目前的缺点是我必须在包管理器控制台中(仅)使用 2 个命令手动迁移。但同时,这不是动态发生的事实也很好,因为这可以防止对我的数据库进行可能的预期更改。此外,一切都完美无缺。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多