ASP.NET MVC 3应用程序使用Ninject,实体框架4代码优先CTP 5,模式

Rus*_*ino 17 ninject entity-framework-4 asp.net-mvc-3

我试图用上述技术构建一些基础项目.我希望获得最大的灵活性和可测试性,因此我尝试使用模式将其作为未来项目的基础.然而,它似乎有些不对劲,我真的需要帮助.所以我有两个问题:

  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; }
}
Run Code Online (Sandbox Code Playgroud)

评论

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; }
}
Run Code Online (Sandbox Code Playgroud)

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

通用存储库

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();
 }
}
Run Code Online (Sandbox Code Playgroud)

对于特定的操作,我使用一个接口:

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

所以我从抽象类加上具体操作得到了泛型操作:

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>();
 }
}
Run Code Online (Sandbox Code Playgroud)

正如你所想,我还使用数据库工厂将产生数据库上下文:

数据库工厂

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();
 }
}
Run Code Online (Sandbox Code Playgroud)

一次性(一些扩展方法......)

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()
 {
 }
}
Run Code Online (Sandbox Code Playgroud)

数据库

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 
 }
 */
}
Run Code Online (Sandbox Code Playgroud)

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

工作单位

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();
 }
}
Run Code Online (Sandbox Code Playgroud)

我还绑定了Ninject接口:

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>();
  }
 }
}
Run Code Online (Sandbox Code Playgroud)

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

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

    }
Run Code Online (Sandbox Code Playgroud)

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

ServerVersion = '(((System.Data.Entity.DbContext (_database)).Database.Connection).ServerVersion' threw an exception of type 'System.InvalidOperationException'
Run Code Online (Sandbox Code Playgroud)

我做的是正确的事吗?我建造的东西有什么不对吗?

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

我用 :

  • 具有Code-First的实体框架4

  • ASP.NET MVC 3

  • Ninject作为DI容器

  • SQL Server Express(不是R2)

  • Visual Studio 2010 Web Express

Rus*_*ino 11

好恶.这个偷偷摸摸.其实我不知道ninject太多所以我不能马上解决它.

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

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

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

在工作单元上调用commit时,由于审查不在此数据库实例上而没有保存任何内容.实际上,由于ninject发送了一个新实例,因此称为databasefactory的工作单元导致创建一个新实例.

要修复它,只需使用:

 Bind<IDatabaseFactory>().To<DatabaseFactory>().InSingletonScope();
Run Code Online (Sandbox Code Playgroud)

代替

Bind<IDatabaseFactory>().To<DatabaseFactory>();
Run Code Online (Sandbox Code Playgroud)

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

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

  • 您可能希望将InSingletonScope更改为InRequestScope(https://github.com/ninject/ninject/wiki/Object-Scopes).最好不要在Web请求之间维护状态.例如,使用InSingletonScope,我认为两个用户都可以获得相同的实例,您可能不希望这样.默认值为InTransientScope:"每次请求一个新类型时都会创建.",这导致了您的错误. (9认同)

Gre*_*egL 5

一个小小的观察:通过使你EFRepositoryBaseIReviewRepository有方法即返回IEnumerable<>,而不是一个IQueryable<>,可以防止添加过滤器表达式/约束或投影左右就到查询后续方法.相反,通过使用IEnumerable<>,你会做任何后续滤波(例如,使用LINQ扩展方法)对整个结果集,而不是让这些操作影响,并简化对获取数据存储运行的SQL语句.

换句话说,您正在进行Web服务器级别的进一步过滤,而不是在可能的情况下真正属于它的数据库级别.

然后,这可能是有意的 - IEnumerable<>如果您确实希望阻止函数的调用者修改生成的SQL等,则有时使用是有效的.

  • 这个评论澄清了我对项目架构的一些问题.我碰巧认为我需要在`IEnumerable <>`上使用`IQueryable <>`,但在推理上并没有100%清楚.谢谢. (2认同)