【问题标题】:Join multiple tables when using a generic repository in C#在 C# 中使用通用存储库时加入多个表
【发布时间】:2015-11-12 14:35:17
【问题描述】:

我有一个 .Net 解决方案,其中包含多个项目,如下所示。

  • 业务(解决方案文件夹)
    • 核心(项目)
    • 接口(项目)
  • 数据(解决方案文件夹)
    • 数据(项目)
  • 演示文稿(解决方案文件夹)
    • AdminPanel(项目)
  • 共享(解决方案文件夹)
    • 普通(项目)

我已经使用 Unity(即 Microsoft.Practices.Unity)实现了具有 IoC 层的通用存储库模式。一切都很好,除非我想做多表简单或复杂的连接。我尝试了很多不同的方法。关于存储库中的连接,我已经浏览了这里的每一个现有线程,但它们都对我的需要没有帮助。

这是我的存储库类。

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class, IEntity
    {
        protected BuyatimeshareModel Context { get; private set; }

        public Repository(IContextFactory contextFactory)
            : this(contextFactory.Get())
        {
        }

        protected Repository(BuyatimeshareModel context)
        {
            context.Database.Log = message => { Common.Logging.Log(message); };
            Context = context;
        }
        IDbSet<TEntity> DbSet
        {
            get
            {
                return Context.Set<TEntity>();
            }
        }
        public TEntity Add(TEntity instance)
        {
            DbSet.Add(instance);
            Context.SaveChanges();
            return instance;
        }

        public void Remove(TEntity instance)
        {
            DbSet.Remove(instance);
            Context.SaveChanges();
        }
        public TEntity FindOne(Expression<Func<TEntity, bool>> predicate)
        {
            return DbSet.AsQueryable().FirstOrDefault(predicate);
        }

        public IEnumerable<TEntity> All()
        {
            return DbSet.AsQueryable();
        }

        public IEnumerable<TEntity> Query() { IQueryable<TEntity> query = DbSet; return query.ToList(); }

        public IEnumerable<TEntity> FindAll(Expression<Func<TEntity, bool>> predicate)
        {
            return DbSet.AsQueryable().Where(predicate);
        }

        public int Count()
        {
            return DbSet.AsQueryable().Count();
        }

        public int Count(Expression<Func<TEntity, bool>> predicate)
        {
            return DbSet.AsQueryable().Count(predicate);
        }

        public bool Exists(Expression<Func<TEntity, bool>> predicate)
        {
            return DbSet.AsQueryable().Any(predicate);
        }
    }

这是我的 IoC 层。

public class UnityControllerFactory : DefaultControllerFactory
    {
        IUnityContainer container;

        public UnityControllerFactory(IUnityContainer container)
        {
            this.container = container;
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            try
            {
                if (controllerType == null)
                    throw new ArgumentNullException("controllerType");

                if (!typeof(IController).IsAssignableFrom(controllerType))
                    throw new ArgumentException(string.Format(
                        "Type requested is not a controller: {0}", controllerType.Name),
                        "controllerType");

                return container.Resolve(controllerType) as IController;
            }
            catch { return null; }
        }

        public static void Configure()
        {
            IUnityContainer container = new UnityContainer();
            /*string connectionString = ConfigurationManager.ConnectionStrings["Connection"].ConnectionString;*/
            container.RegisterType<IContextFactory, ContextFactory>(new ContainerControlledLifetimeManager())//, new InjectionConstructor(connectionString))
                     .RegisterType<IUnitOfWork, UnitOfWork>(new ContainerControlledLifetimeManager())
                     .RegisterType<IAdminService, AdminService>()

                     .RegisterType(typeof(IRepository<>), typeof(Repository<>));
            ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(container));
        }
    }

最后是我的服务层,我尝试从存储库层查询一些数据。

public class AdminService : IAdminService
    {
        private readonly IRepository<press_releases> pressReleasesRepo;
        private readonly IRepository<tblads> adsRepo;
        private readonly IRepository<tblresorts> resortsRepo;

        public AdminService(IRepository<press_releases> _pressReleasesRepo, IRepository<tblads> _adsRepo, IRepository<tblresorts> _resortsRepo)
        {
            pressReleasesRepo = _pressReleasesRepo;
            adsRepo = _adsRepo;
            resortsRepo = _resortsRepo;
        }
        public List<press_releases> Test()
        {
            var data = pressReleasesRepo.FindAll(p => p.pr_id > 0);
            var data1 =
                (from a in adsRepo.Query()
                    join r in resortsRepo.Query() on a.resort_id equals r.resort_id
                    where a.ad_id == 413
                    select new
                    {
                        OwnerId = a.owner_id,
                        ResortName = r.name,
                        AdId = a.ad_id,
                        AskingPrice = a.askingPriceInt
                    }).ToList();

            var model = data.ToList();
            return model;
        }
    }

现在我们有两个查询。 数据数据1

data 只是用一个条件查询一个表,并按预期返回结果。一切都很好。

但是,在 data1 中,它在技术上也准确地返回了我想要的最终结果,但是我也有一个原始 SQL 记录器来查看幕后发生的事情以及它正在做的事情是使用 select * 等效语句分别查询 ads 表和 Resorts 表,然后当结果从两个表返回时,它在内存中应用连接并在通过 where 子句过滤后返回结果。所以基本上它扫描了大约 100000 行,即使我有 join 和 where 子句,最后它返回一个广告 id 为 413 的单行。

需要注意的一点是,我在为返回 IEnumerable 的 data1 执行连接时调用了存储库类的 Query 方法。我无法将其更改为 IQueryable,因为随后引发了异常,其中包含一些消息,例如 linq 查询引用了不同的上下文。

任何人都可以指导我修改此代码,以便我可以在存储库之间应用真正的 sql 连接,或者是否有办法通过添加中间层作为桥梁来修改此代码。从这一点开始,我需要一些东西。我一直在到处寻找,但无济于事。我敢肯定,我不是这个世界上第一个遇到这个问题的人。肯定会有其他人遇到类似问题,并且可能有人找到了真正的方法。

我的工具和技术如下。

  • Visual Studio 2013
  • .Net 框架 4.0
  • MySQL 数据库
  • 适用于 Visual Studio 1.2.4 的 MySQL
  • MySQL 连接器 6.9.6
  • 实体框架 6
  • Windows 10 专业版 x64

如果有什么我可能遗漏的地方,请告诉我。

任何帮助将不胜感激。

编辑:

这是 ContextFactory 类

public class ContextFactory : IContextFactory
    {
        private bool _isDisposed;
        private SomeModel _context;

        public ContextFactory()
        { }

        public SomeModel Get()
        {
            _context = new SomeModel();
            return _context;
        }

        ~ContextFactory()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_isDisposed && disposing && (_context != null))
                _context.Dispose();

            _isDisposed = true;
        }
    }

【问题讨论】:

    标签: c# mysql linq entity-framework-6 repository-pattern


    【解决方案1】:

    这是您在存储库中使用的数据1 中使用的方法。

    public IEnumerable<TEntity> Query() { IQueryable<TEntity> query = DbSet; return query.ToList(); }
    

    每当你点击 '.ToList()' 时,它都会点击数据库。

    如果您想先创建查询然后执行,请使用存储库的“DbSet”属性。

    【讨论】:

    • 非常感谢您的回复。我在之前的问题中已经提到过这一点,我知道我返回的是 IEnumerable 类型而不是 IQueryable ,即立即查询与延迟查询。但这给了我一个错误,即具有不同的上下文。因此,我因此无法跳过 .ToList() 。我希望这能解释我的问题。
    • 能否贴出ContextFactory相关的代码。您正在从中获取 DBContext,它是每次都创建该上下文还是被缓存。
    • 在您的 ContextFactory 中添加一个 GUID 字段并将其记录在每个 Get 上,并查看它们是否相同。更多测试方法见此链接ladislavmrnka.com/2011/03/unity-build-in-lifetime-managers
    • 很好的建议。我想我可以从一开始就看到问题可能出在哪里。我已经用 ContextFactory 类的内容更新了我的问题。它确实为每个 get() 调用返回一个新实例。我想我必须改变它的实现并使它成为一个单例类。 Unity Container 本身被要求将 ContextFactory 注册为单例,但我从它返回的模型在每次调用的基础上都是唯一的,它不应该是。
    猜你喜欢
    • 1970-01-01
    • 2018-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-27
    相关资源
    最近更新 更多