2011-01-24 4 views
17

Ive ha provato a creare un progetto di base con le tecnologie di cui sopra. Volevo la massima flessibilità e verificabilità, così ho provato a utilizzare i pattern lungo il percorso per renderlo una base per i progetti futuri. Tuttavia, sembra che qualcosa sia sbagliato o qualsiasi altra cosa e ho davvero bisogno di aiuto qui. Quindi ho due domande:ASP.NET MVC 3 Applicazione che utilizza Ninject, Entity Framework 4 Code-First CTP 5, Pattern

1- C'è qualcosa di sbagliato nel mio codice corrente? Ho applicato i pattern correttamente? Qualche suggerimento o raccomandazione che mi porterebbe nella giusta direzione?

2- Perché questo codice si collega effettivamente al database, lo crea, ma non supporta l'inserimento anche se eseguo l'operazione di correzione? (Vedi alla fine del post per ulteriori informazioni su questo errore) ** ** fisso

Credo che questo potrebbe anche aiutare gli altri in quanto i havent trovato sufficienti informazioni per poter fare qualcosa in modo corretto. Sono abbastanza sicuro che molte persone provano a farlo nel modo giusto e non sono sicuro come me se quello che sto facendo è giusto.

ho due entità: commento e recensione

COMMENTO

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

RASSEGNA

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

ho costruito un deposito di base per ciascuno di loro in questo modo:

DEPOSITO GENERICO

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

Per operazioni specifiche, io uso un'interfaccia:

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

Quindi io sono sempre le operazioni di farmaci generici dalla classe astratta più le operazioni specifiche:

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

Come hai capito, uso anche un database di fatto ry produrrà il contesto di database:

DATABASE FABBRICA

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

MONOUSO (Alcuni metodi di estensioni ...)

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

DATABASE

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

E per finire, ho la mia unità di lavoro ....

UNITA DI LAVORO

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

Ho anche rilegato utilizzando Ninject le interfacce:

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

Tuttavia, quando chiamo nel costruttore (The azione predefinita) ...

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

    } 

Questo sembra creare il database ma non inserisce nulla nel database in EF4. E sembra che io possa capito il problema .. mentre guardando l'oggetto di database .. lo stato della connessione è chiusa e la versione del server generano un'eccezione di questo genere:

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

sto facendo le cose giuste? C'è qualcosa di sbagliato in ciò che è stato costruito?

Anche se si consiglia di conoscere il codice che ho pubblicato, sarei felice. Sto solo cercando di imparare il modo giusto per creare qualsiasi tipo di applicazione in MVC 3. Voglio iniziare bene.

io uso:

  • Entity Framework 4 con Codice-First

  • ASP.NET MVC 3

  • Ninject come DI Container

  • SQL Server Express (non R2)

  • Visual Studio 2010 Web Express

Grazie mille per il vostro aiuto!

+0

Dove hai preso il picchiettio per questo con Entity ? Sono interessato a guardare qualcosa di simile. Ho visto alcuni modelli simili nell'applicazione Shrinkr scritta da Kazi Manzur Rashid @ http://shrinkr.codeplex.com/ –

+0

Penso che questo codice sia stato originariamente da http://myfinance.codeplex.com/ – woggles

+1

Nope ive riunito questo codice da altri post in questo posto più qualcuno con cui ho parlato che è davvero esperto nella zona. Era mia intenzione mettere qui tutto il mio codice per aiutare gli altri a darsi da fare. – Rushino

risposta

11

Eww. Questo era subdolo. In realtà non lo so molto, quindi non riuscivo a capirlo subito.

Ho trovato la soluzione per la SECONDA domanda che era correlata all'errore trovando che ninject effettivamente scatta due istanze di DatabaseFactory, una per il repository e una per l'unità di lavoro. In realtà, l'errore non era il problema. Si è trattato di un errore interno nel database degli oggetti, ma è normale che io lo ritenga da quando utilizzo Entity Framework.

Il vero problema era che Ninject stava vincolando due diverse istanze di IDatabaseFactory che portavano a 2 connessioni aperte.

La revisione è stata aggiunta alla prima serie in _reviewRepostory che utilizzava la prima istanza del database.

Quando si chiama commit sull'unità di lavoro, non ha salvato nulla a causa del fatto che la revisione non si trovava su questa istanza di database. In effetti, l'unità di lavoro ha chiamato databasefactory che ha portato alla creazione di una nuova istanza poiché ninject ne ha inviato una nuova istanza.

per risolvere il problema è sufficiente utilizzare:

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

invece di

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

E ora tutto il lavoro di sistema in modo corretto!

Ora, mi piacerebbe qualche risposta riguardo alla prima domanda che c'era se c'era qualcosa di sbagliato nel mio codice corrente? Ho applicato i pattern correttamente? Qualche suggerimento o raccomandazione che mi porterebbe nella giusta direzione?

+9

È possibile modificare InSingletonScope in InRequestScope (https://github.com/ninject/ninject/wiki/Object-Scopes). È preferibile non mantenere lo stato tra richieste web. Per esempio. Con InSingletonScope penso che due utenti possano ottenere entrambi la stessa istanza, potreste non volerlo. Il valore predefinito è InTransientScope: "Una nuova istanza del tipo verrà creata ogni volta che ne viene richiesta una.", Che causava il tuo errore. –

+0

potresti spiegare come stai smaltendo le tue entità, poiché non vedo alcun metodo all'interno del controller che chiama dispose. Ho visto che c'è una classe di nome Dispose, ma non sono in grado di capire come quella classe entri in scena e ne disponga. –

+0

Il metodo di smaltimento è solo per la fabbrica, – Rushino

5

Una piccola osservazione: per avere il vostro EFRepositoryBase e IReviewRepository hanno metodi che restituiscono un IEnumerable<> invece di un IQueryable<>, si impedisce metodi successivi di aggiungere filtri espressioni/vincoli o proiezioni o così via alla query. Invece, usando IEnumerable<>, si farà qualsiasi filtraggio successivo (ad esempio usando i metodi di estensione LINQ) sul set di risultati completo, piuttosto che consentire a tali operazioni di influenzare e semplificare l'istruzione SQL che viene eseguita contro il datastore.

In altre parole, si sta effettuando un ulteriore filtraggio a livello di server Web, non a livello di database a cui appartiene realmente se possibile.

Poi di nuovo, questo può essere intenzionale - a volte utilizzando IEnumerable<> è valido se si vuole evitare che i chiamanti della vostra funzione di modificare il codice SQL che viene generato, ecc

+2

Questo commento ha chiarito alcune domande sull'architettura del mio progetto. Mi è capitato di pensare che dovevo usare 'IQueryable <>' su 'IEnumerable <>', ma non era chiaro al 100% sul ragionamento. Grazie. – Ecnalyr