【问题标题】:ASP.NET MVC 3 Application using Ninject, Entity Framework 4 Code-First CTP 5, Patterns使用 Ninject、Entity Framework 4 Code-First CTP 5、模式的 ASP.NET MVC 3 应用程序
【发布时间】:2018-06-11 02:11:43
【问题描述】:

我尝试使用上述技术构建一些基础项目。我想要最大的灵活性和可测试性,因此我尝试在此过程中使用模式来将其作为未来项目的基础。然而,似乎 出了点问题或其他什么,我真的需要帮助。所以我有两个问题:

  1. 我当前的代码有什么问题吗?我正确地应用了模式?有什么建议或建议可以引导我朝着正确的方向前进吗?

  2. 为什么这段代码实际上连接到数据库并创建它,但即使我执行了更正操作也不支持插入? (有关此错误的详细信息,请查看帖子末尾)已修复

我相信这也可以帮助其他人,因为我没有找到足够的信息来正确地弥补一些东西。我很确定很多人都试图以正确的方式做事,但不确定我所做的是否正确。

我有两个实体:评论和评论

评论

public class Comment
{
 [Key]
 public virtual int Id { get; set; }

 public virtual string Name { get; set; }
 public virtual string Author { get; set; }
 public virtual string Body { get; set; }
}

评论

public class Review
{
 [Key]
 public virtual int Id { get; set; }

 public virtual string Name { get; set; }
 public virtual string Author { get; set; }
 public virtual string Body { get; set; }
 public virtual bool Visible { get; set; }

 public IEnumerable<Comment> Comments { get; set; }
}

我以这种方式为每个人建立了一个基础存储库:

通用存储库

public abstract class EFRepositoryBase<T> : IRepository<T> where T : class
{
 private Database _database;
 private readonly IDbSet<T> _dbset;

 protected IDatabaseFactory DatabaseFactory { get; private set; }
 protected Database Database { get { return _database ?? (_database = DatabaseFactory.Get()); } }

 public EFRepositoryBase(IDatabaseFactory databaseFactory)
 {
  DatabaseFactory = databaseFactory;
  _dbset = Database.Set<T>();
 }

 public virtual void Add(T entity)
 {
  _dbset.Add(entity);
 }

 public virtual void Delete(T entity)
 {
  _dbset.Remove(entity);
 }

 public virtual T GetById(long id)
 {
  return _dbset.Find(id);
 }

 public virtual IEnumerable<T> All()
 {
  return _dbset.ToList();
 }
}

对于具体操作,我使用了一个接口:

public interface IReviewRepository : IRepository<Review> {
 // Add specific review operations
 IEnumerable<Review> FindByAuthor(string author);
}

所以我从抽象类中获取泛型操作以及特定操作:

public class EFReviewRepository : EFRepositoryBase<Review>, IReviewRepository
{
 public EFReviewRepository(IDatabaseFactory databaseFactory) 
  : base(databaseFactory)
 { }

 public IEnumerable<Review> FindByAuthor(string author)
 {
  return base.Database.Reviews.Where(r => r.Author.StartsWith(author))
   .AsEnumerable<Review>();
 }
}

如你所见,我还使用数据库工厂来生成数据库上下文:

数据库工厂

public class DatabaseFactory : Disposable, IDatabaseFactory
{
 private Database _database;

 public Database Get()
 {
  return _database ?? (_database = new Database(@"AppDb"));
 }

 protected override void DisposeCore()
 {
  if (_database != null)
   _database.Dispose();
 }
}

DISPOSABLE(一些扩展方法...)

public class Disposable : IDisposable
{
 private bool isDisposed;

 ~Disposable()
 {
  Dispose(false);
 }

 public void Dispose()
 {
  Dispose(true);
  GC.SuppressFinalize(this);
 }
 private void Dispose(bool disposing)
 {
  if (!isDisposed && disposing)
  {
   DisposeCore();
  }

  isDisposed = true;
 }

 protected virtual void DisposeCore()
 {
 }
}

数据库

public class Database : DbContext
{
 private IDbSet<Review> _reviews;

 public IDbSet<Review> Reviews
 {
  get { return _reviews ?? (_reviews = DbSet<Review>()); }
 }

 public virtual IDbSet<T> DbSet<T>() where T : class
 {
  return Set<T>();
 }

 public Database(string connectionString)
  : base(connectionString)
 {
  //_reviews = Reviews;
 }

 public virtual void Commit()
 {
  base.SaveChanges();
 }

 /*
 protected override void OnModelCreating(ModelBuilder modelBuilder)
 {
  // TODO: Use Fluent API Here 
 }
 */
}

最后,我有我的工作单元......

工作单位

public class UnitOfWork : IUnitOfWork
{
 private readonly IDatabaseFactory _databaseFactory;
 private Database _database;

 public UnitOfWork(IDatabaseFactory databaseFactory)
 {
  _databaseFactory = databaseFactory;
 }

 protected Database Database
 {
  get { return _database ?? (_database = _databaseFactory.Get()); }
 }

 public void Commit()
 {
  Database.Commit();
 }
}

我还使用 Ninject 绑定了接口:

忍者控制器工厂

public class NinjectControllerFactory : DefaultControllerFactory
{
 // A Ninject "Kernel" is the thing that can supply object instances
 private IKernel kernel = new StandardKernel(new ReviewsDemoServices());

 // ASP.NET MVC calls this to get the controller for each request
 protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
 {
  if (controllerType == null)
   return null;
  return (IController)kernel.Get(controllerType);
 }

 private class ReviewsDemoServices : NinjectModule
 {
  public override void Load()
  {
   // Bindings...
   Bind<IReviewRepository>().To<EFReviewRepository>();
   Bind<IUnitOfWork>().To<UnitOfWork>();
   Bind<IDatabaseFactory>().To<DatabaseFactory>();
   Bind<IDisposable>().To<Disposable>();
  }
 }
}

但是,当我在构造函数中调用时(默认操作)...

public class ReviewController : Controller
    {
        private readonly IReviewRepository _reviewRepository;
        private readonly IUnitOfWork _unitOfWork;

        public ReviewController(IReviewRepository postRepository, IUnitOfWork unitOfWork)
        {
            _reviewRepository = postRepository;
            _unitOfWork = unitOfWork;
        }

        public ActionResult Index()
        {
            Review r = new Review { Id = 1, Name = "Test", Visible = true, Author = "a", Body = "b" };
            _reviewRepository.Add(r);
            _unitOfWork.Commit();

            return View(_reviewRepository.All());
        }

    }

这似乎创建了数据库,但没有在 EF4 的数据库中插入任何内容。看来我可能发现了问题..在查看数据库对象时..连接状态已关闭,服务器版本抛出此类异常:

ServerVersion = '(((System.Data.Entity.DbContext (_database)).Database.Connection).ServerVersion' threw an exception of type 'System.InvalidOperationException'

我在做正确的事吗?我建造的东西有什么问题吗?

另外,如果您对我发布的代码有任何建议,我会很高兴。我只是想学习在 MVC 3 中构建任何类型的应用程序的正确方法。我想要一个好的开始。

我用:

  • Entity Framework 4 with Code-First

  • ASP.NET MVC 3

  • Ninject 作为 DI 容器

  • SQL Server Express(非 R2)

  • Visual Studio 2010 网络速递

【问题讨论】:

  • 你从哪里得到这个关于 Entity 的模式?我有兴趣看看这样的东西。我在 Kazi Manzur Ra​​shid @shrinkr.codeplex.com 编写的收缩器应用程序中看到了类似的模式
  • 我认为这段代码最初来自myfinance.codeplex.com
  • 没有,我从这个地方周围的其他帖子以及与我交谈过的人那里收集了这个代码,他是该领域的真正专家。我打算把我所有的代码放在这里帮助其他人。
  • 您是否有理由暴露您的工作单元,什么时候应该在包含工作单元的存储库中?我希望你调用你的存储库服务来更新、插入和保存... RepositoryService.Insert(entity); RepositoryService.Save();

标签: entity-framework-4 asp.net-mvc-3 ninject


【解决方案1】:

哎呀。这个是偷偷摸摸的。实际上我不太了解ninject,所以我无法立即弄清楚。

我找到了与错误相关的第二个问题的解决方案,发现 ninject 实际上拍摄了两个 DatabaseFactory 实例,一个用于存储库,一个用于工作单元。实际上,错误不是问题。这是对象数据库中的内部错误,但我认为这是正常的,因为我使用的是实体框架。

真正的问题是 Ninject 绑定了两个不同的 IDatabaseFactory 实例,导致 2 个连接打开。

评论已添加到 _reviewRepostory 中的第一个集合中,该集合正在使用数据库的第一个实例。

在工作单元上调用 commit 时,由于审查不在此数据库实例上,因此它什么也没保存。事实上,工作单元称为 databasefactory 导致创建一个新实例,因为 ninject 发送了它的一个新实例。

要修复它,只需使用:

 Bind<IDatabaseFactory>().To<DatabaseFactory>().InSingletonScope();

而不是

Bind<IDatabaseFactory>().To<DatabaseFactory>();

现在所有系统都可以正常工作了!

现在,我会喜欢关于第一个问题的答案,即我当前的代码是否有任何问题?我正确地应用了模式?有什么建议或建议可以引导我朝着正确的方向前进吗?

【讨论】:

  • 您可能希望将 InSingletonScope 更改为 InRequestScope (github.com/ninject/ninject/wiki/Object-Scopes)。最好不要在 web 请求之间保持状态。例如。使用 InSingletonScope,我认为两个用户都可以获得相同的实例,您可能不希望这样。默认值为 InTransientScope:“每次请求时都会创建该类型的新实例。”,这导致了您的错误。
  • 你能解释一下你是如何处理你的实体的,因为我没有看到控制器内部有任何调用 dispose 的方法。我看到有一个名为 Dispose 的类,但我无法弄清楚该类是如何出现并进行处理的。
  • dispose方法只针对工厂,
  • 谢谢你,只是无法弄清楚我的工作单元出了什么问题!
  • 正如 Matthijs Wessels 所说.. 对于 Web 应用程序,你们应该坚持使用 InRequestScope。这很重要!对于单线程和多线程应用程序,您必须找到其他策略。这种情况在桌面应用程序中不是很好。
【解决方案2】:

一个小观察:通过让您的 EFRepositoryBaseIReviewRepository 具有返回 IEnumerable&lt;&gt; 而不是 IQueryable&lt;&gt; 的方法,可以防止后续方法向查询添加过滤器表达式/约束或投影等.相反,通过使用 IEnumerable&lt;&gt;,您将对整个结果集执行任何后续过滤(例如使用 LINQ 扩展方法),而不是允许这些操作影响和简化针对数据存储运行的 SQL 语句。

换句话说,您是在网络服务器级别进行进一步过滤,而不是尽可能在它真正属于的数据库级别进行过滤。

再一次,这可能是故意的 - 如果您确实想阻止函数的调用者修改生成的 SQL 等,有时使用 IEnumerable&lt;&gt; 是有效的。

【讨论】:

  • 这条评论解决了我对项目架构的一些疑问。我碰巧在想我需要使用IQueryable&lt;&gt; 而不是IEnumerable&lt;&gt;,但不是100% 清楚推理。谢谢。
猜你喜欢
  • 2011-06-06
  • 2011-03-18
  • 2011-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-26
相关资源
最近更新 更多