Подтвердить что ты не робот

Приложение ASP.NET MVC 3 с использованием Ninject, Entity Framework 4 Code-First CTP 5, шаблоны

ive попытался построить некоторый базовый проект с использованием вышеперечисленных технологий. Мне нужна максимальная гибкость и возможность тестирования, поэтому я попытался использовать шаблоны на этом пути, чтобы сделать это основой будущих проектов. Однако, похоже что-то не так или что-то еще, и мне действительно нужна помощь здесь. Поэтому у меня есть два вопроса:

1 - Что-то не так с моим текущим кодом? Я правильно применял шаблоны? Любые предложения или рекомендации, которые приведут меня в правильном направлении?

2 - Почему этот код действительно подключается к базе данных, создает его, но не поддерживает вставку, даже если я выполняю операцию исправления? (Посмотрите на конец сообщения, чтобы узнать подробности об этой ошибке) ** FIXED **

Я считаю, что это также может помочь другим, поскольку я не нашел достаточной информации, чтобы что-то сделать правильно. Я уверен, что много poeple пытаются сделать это правильно и не уверены, как я, если я делаю правильно.

У меня есть два объекта: Комментарий и обзор

К.П

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

Я создал базовый репозиторий для каждого из них следующим образом:

GENERIC REPOSITORY

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

Как вы поняли, я также использую базу данных factory создаст контекст базы данных:

БАЗА ДАННЫХ FACTORY

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 
 }
 */
}

И чтобы закончить, у меня есть моя часть работы....

UNIT OF WORK

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 интерфейсы:

КОНТРОЛЛЕР NINJECT FACTORY

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'

Я делаю правильные вещи? Есть ли что-то неправильное в том, что построено ive?

Также, если у вас есть рекомендация по поводу кода, который я опубликовал, я был бы рад. Я просто пытаюсь изучить правильный способ создания любого приложения в MVC 3. Я хочу начать хорошо.

Я использую:

  • Entity Framework 4 с Code-First

  • ASP.NET MVC 3

  • Ninject как контейнер DI

  • SQL Server Express (не R2)

  • Visual Studio 2010 Web Express

Спасибо за помощь!

4b9b3361

Ответ 1

Eww. Этот был подлый. На самом деле я не знаю ничего, поэтому я не мог понять это сразу.

Я нашел решение для ВТОРОГО вопроса, связанного с ошибкой, обнаружив, что ninject фактически снимает два экземпляра DatabaseFactory, один для репозитория и один для единицы работы. Собственно, ошибка не была проблемой. Это была внутренняя ошибка в базе данных объектов, но ее нормальная я думаю, поскольку я использую Entity Framework.

Реальная проблема заключалась в том, что Ninject связывает два разных экземпляра IDatabaseFactory, которые приводят к открытию 2-х соединений.

Обзор был добавлен в первый набор в _reviewRepostory, который использовал первый экземпляр базы данных.

При вызове commit на единице работы.. он ничего не сохранил из-за того, что в этом экземпляре базы данных не было обзора. Фактически, единица работы называется databasefactory, которая приводит к созданию нового экземпляра, поскольку ninject отправил новый экземпляр.

Чтобы исправить это, просто используйте:

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

вместо

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

И теперь вся система работает правильно!

Теперь мне понравились бы ответы на первый вопрос, который был, если что-то не так с моим текущим кодом? Я правильно применял шаблоны? Любые предложения или рекомендации, которые приведут меня в правильном направлении?

Ответ 2

Одно небольшое наблюдение: имея методы EFRepositoryBase и IReviewRepository, которые возвращают IEnumerable<> вместо IQueryable<>, вы запрещаете последующим методам добавлять выражения фильтра/ограничения или прогнозы или так далее к запросу, Вместо этого, используя IEnumerable<>, вы будете выполнять любую последующую фильтрацию (например, используя методы расширения LINQ) в полном наборе результатов, а не позволять этим операциям влиять и упрощать инструкцию SQL, которая запускается против хранилища данных.

Другими словами, вы выполняете дополнительную фильтрацию на уровне веб-сервера, а не на уровне базы данных, где это действительно возможно, если возможно.

Опять же, это может быть преднамеренным - иногда использование IEnumerable<> является допустимым, если вы хотите, чтобы вызывающие функции вашей функции не изменяли созданный SQL и т.д.