数据库未使用存储库模式和EF Code First更新

时间:2013-07-08 17:26:50

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

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

我的应用程序设计如下。

  • 网站
  • Website.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是第二个,它只是一个简单的POCO类,具有Id和一些属性。

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();'时添加一只手表我得到以下例外;

enter image description here

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

更新1:

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

Database.SetInitializer<MyContext>(null);

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

现在我在调试'_portfolioService.GetAll();'时遇到了这个错误和堆栈跟踪打电话给家庭控制器

错误:

  

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

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()

1 个答案:

答案 0 :(得分:0)

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

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

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

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个命令手动迁移。但同时,这不会动态发生的事实也很好,因为这可以防止对我的数据库进行有害的更改。此外,一切都很完美。