首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >存储库模式+工作模式单元+ MVC3 + EF4 -定义模型数据以处理所有问题

存储库模式+工作模式单元+ MVC3 + EF4 -定义模型数据以处理所有问题
EN

Stack Overflow用户
提问于 2012-04-03 01:16:36
回答 1查看 2K关注 0票数 3

我完成了关于工作单元和存储库模式的pluralsight教程。我试图将两者结合在一个MVC和Ef4实现中。本教程有一个小EF模型,只有2个实体。因此,匹配这两个实体的对象是用ICollections定义的,用于导航、属性等,就像在代码中一样,但是这些对象与实体框架之间没有联系。这些对象被聚合为工作单元中的存储库。但这就是我开始提出以下问题的地方。

当您可以使用实体框架中的对象时,

  1. 为什么要创建已经在实体框架中的对象的基本复制对象?你不能吗?
  2. 如果我创建了这些对象..。我要画出整个ef模型吗?有了导航属性的ICollections,如果对象不完全匹配,我就会产生错误,并且它基本上会在大部分模型中继续级联。这个模型相当大,涉及十几个实体,我不想创建存储库中不使用的实体。如果这些是发送到视图的类,那么
  3. .您的视图模型类不是应该简化实际模型类的版本吗?这意味着,在我的控制器中,我将使用工作单元和存储库类来检索数据,然后剥离它们,只将某些数据块传递给视图模型。这意味着,我们刚才通过使用工作单元和存储库模式获得的所有清洁度现在又开始消失了。

我有以下代码..。

代码语言:javascript
复制
namespace Data
{

    public interface IRepository<T>
                where T : class//, IEntity
    {
        IQueryable<T> FindAll();
        IQueryable<T> Find(Expression<Func<T, bool>> predicate);
        //T FindById(int id);
        void Add(T newEntity);
        void Remove(T entity);
    }

    public class SqlRepository<T> : IRepository<T>
                                where T : class//, IEntity
    {

        public SqlRepository(ObjectContext context)
        {
            _objectSet = context.CreateObjectSet<T>();
        }

        public IQueryable<T> Find(Expression<Func<T, bool>> predicate)
        {
            return _objectSet.Where(predicate);
        }

        public void Add(T newEntity)
        {
            _objectSet.AddObject(newEntity);
        }  

        public void Remove(T entity)
        {
            _objectSet.DeleteObject(entity);
        }


        public IQueryable<T> FindAll()
        {
            return _objectSet;
        }

        protected ObjectSet<T> _objectSet;
    }

 }



public class SqlUnitOfWork : IUnitOfWork
{

    public SqlUnitOfWork()
    {
        var connectionString =
            ConfigurationManager
                .ConnectionStrings[ConnectionStringName]
                .ConnectionString;

        _context = new ObjectContext(connectionString);
        _context.ContextOptions.LazyLoadingEnabled = true;
    }

    public IRepository<Domain.Project> Projects
    {
        get
        {
            if (_projects == null)
            {
                _projects = new SqlRepository<Domain.Project>(_context);
            }
            return _projects;
        }
    }


    public void Commit()
    {
        _context.SaveChanges();
    }

    SqlRepository<Domain.Project> _projects = null;
    //SqlRepository<TimeCard> _timeCards = null;
    readonly ObjectContext _context;
    const string ConnectionStringName = "Entities";
}
} 



namespace Domain
{
    public interface IRepository<T>
                where T : class//, IEntity
    {
        IQueryable<T> FindAll();
        IQueryable<T> Find(Expression<Func<T, bool>> predicate);
        //T FindById(int id);
        void Add(T newEntity);
        void Remove(T entity);
    }


    public interface IUnitOfWork
    {
        IRepository<Project> Projects { get; }
        //IRepository<TimeCard> TimeCards { get; }
        void Commit();
    }
}

然后我以以下方式声明了我的域类..。也在域命名空间中。

代码语言:javascript
复制
public class Project //: IEntity
{
    public virtual int ProjectId { get; set; }
    public virtual int UserId { get; set; }
    public virtual int CategoryId { get; set; }
    public virtual string Name { get; set; }
    public virtual string Description { get; set; }
    // nav prop - do I have to have these?
    public virtual ICollection<Image> Images { get; set; }
    public virtual ICollection<WatchedProject> WatchedProjects { get; set; }
    public virtual ICollection<RequestForProposal> RequestForProposals { get; set; }
    public virtual ICollection<Message> Messages { get; set; }
    public virtual Category Category { get; set; }
}

现在我只是在测试这个,我有下面的控制台应用程序代码..。

代码语言:javascript
复制
    static void Main(string[] args)
    {
        IUnitOfWork _unitOfWork = new SqlUnitOfWork();
        IRepository<Domain.Project> _repository = _unitOfWork.Projects;

        var prjs =  _unitOfWork.Projects.FindAll( );


        foreach (Domain.Project prj in prjs)
        {
            Console.WriteLine(prj.Description);
        }

        Console.Read();
    }

但是当我运行这一行时

代码语言:javascript
复制
IRepository<Domain.Project> _repository = _unitOfWork.Projects;

是为了..。

代码语言:javascript
复制
_objectSet = context.CreateObjectSet<T>();

我得到了以下例外..。

代码语言:javascript
复制
System.Data.MetadataException was unhandled
  Message=Schema specified is not valid. Errors: 
The relationship 'Model.fk_projects_catetegoryid_categories' was not loaded because the type 'Model.Category' is not available.
The following information may be useful in resolving the previous error:
The required property 'ServiceProviderCategories' does not exist on the type 'Domain.Category'.


The relationship 'Model.FK_Messages_Projects_ProjectId' was not loaded because the type 'Model.Message' is not available.
The following information may be useful in resolving the previous error:
The required property 'MessageId' does not exist on the type 'Domain.Message'.


The relationship 'Model.fk_requestforproposals_projectid_projects' was not loaded because the type 'Model.RequestForProposal' is not available.
The following information may be useful in resolving the previous error:
The required property 'Project' does not exist on the type 'Domain.RequestForProposal'.


The relationship 'Model.FK_WatchedProject_ProjectId_Projects' was not loaded because the type 'Model.WatchedProject' is not available.
The following information may be useful in resolving the previous error:
The required property 'WatchedProjectId' does not exist on the type 'Domain.WatchedProject'.


The relationship 'Model.ProjectImage' was not loaded because the type 'Model.Image' is not available.
The following information may be useful in resolving the previous error:
The required property 'ImageId' does not exist on the type 'Domain.Image'.


  Source=System.Data.Entity
  StackTrace:
       at System.Data.Metadata.Edm.ObjectItemCollection.LoadAssemblyFromCache(ObjectItemCollection objectItemCollection, Assembly assembly, Boolean loadReferencedAssemblies, EdmItemCollection edmItemCollection, Action`1 logLoadMessage)
       at System.Data.Metadata.Edm.ObjectItemCollection.ImplicitLoadAssemblyForType(Type type, EdmItemCollection edmItemCollection)
       at System.Data.Metadata.Edm.MetadataWorkspace.ImplicitLoadAssemblyForType(Type type, Assembly callingAssembly)
       at System.Data.Objects.ObjectContext.GetTypeUsage(Type entityCLRType)
       at System.Data.Objects.ObjectContext.GetEntitySetFromContainer(EntityContainer container, Type entityCLRType, String exceptionParameterName)
       at System.Data.Objects.ObjectContext.GetEntitySetForType(Type entityCLRType, String exceptionParameterName)
       at System.Data.Objects.ObjectContext.CreateObjectSet[TEntity]()
       at Data.SqlRepository`1..ctor(ObjectContext context) in C:\Projects\TestProjectClasses\Data\SqlRepository.cs:line 18
       at Data.SqlUnitOfWork.get_Projects() in C:\Projects\TestProjectClasses\Data\SqlUnitOfWork.cs:line 31
       at TestProjectClasses.Program.Main(String[] args) in C:\Projects\TestProjectClasses\TestProjectClasses\Program.cs:line 26
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

我得到这个异常是因为那些对象还没有在我的域名称空间中创建.但我不想创建每个物体的副本。我做了一些研究,但所有的研究。但似乎我需要在某个时候转换这些物体..。有比在控制器代码中放置一堆转换逻辑更好的方法吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-04-04 07:30:02

  1. 在EF中定义的实体是持久化模型,也就是用什么结构保存数据的模型。应用程序通常有一个模型,用于建模实际的业务问题和解决方案,即行为。存储库接收这个应用程序对象,然后从它们中提取存储所需的任何信息。大多数情况下,这两个模型(app和persistence)非常相似,因为简单的东西是相同的,但在许多情况下,由于意图不同(app模型意图是建模行为,持久性意图是建模存储)。

当加载保存的数据时,存储库从其保存的表单中重新创建应用程序对象,因此它将持久性模型映射到应用程序模型。如果模型非常简单,那么您可以直接使用EF实体,但是如果您知道将来的模型将是不同的,那么从一开始就进行“转换”比较安全,这看起来就像您只是复制实体。

此外,其想法是将应用程序从db access实现细节中分离出来,而EF实体是实现细节。如果明天您决定EF sux和Nhibernate更好,或者您需要一种更轻、更好的方式,您只需要更改存储库实现,而不需要触摸应用程序的其余部分。但是,您提供的代码示例做得很糟糕,因为存储库接口公开了诸如IQueryable这样的实现细节(称为泄漏的IQueryable),而不必映射整个持久性模型。您只映射应用程序当前的需求。如果应用程序想要返回Foo对象,您可以使用Ef获得所有必需的Foo数据(它可以跨多个表,也可以是复杂的查询),并将其映射到Foo。如果Bar对象非常简单,并且几乎是1到1到一个EF实体,那么只需将该实体复制到对象中(这样以后就很容易从EF切换到其他orm)

  • --它们不是。这些是持久性类,而不是视图模型类。大多数EF教程都是非常糟糕的工作,使您认为您正在直接使用EF实体作为您的无处不在的模型。视图模型是一个单独的模型,可以满足视图的需要。它是根据持久化模型构建的。存储库(一个不同的、专门化的查询回购)将直接将查询产生的EF实体映射到视图模型的位元。再次,如果切换到另一个Orm,则不必为视图模型更改任何内容。

正如您所看到的,存储库做了很多工作,并且对维护一个适当的分层应用程序具有重要的响应能力。但是EF是存储库的实现细节,在为应用程序或视图设计模型时,您应该忘记EF或db结构,而只知道存储库接口(根据应用程序的需要设计)。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9985929

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档