У меня есть .Net-решение с несколькими проектами для следующих слоев.Присоединение нескольких таблиц при использовании общего репозитория в C#
- Бизнес (папка решения)
- ядро (проект)
- Интерфейсы (проект)
- данных (папка решения)
- данных (проект)
- Presentation (папка раствора)
- админпанели (проект)
- Shared (папка раствора)
- Common (проект)
Я реализовал общий репозиторий шаблон с уровнем IoC с использованием Unity, то есть Microsoft.Practices.Unity. Все работает отлично, за исключением случаев, когда я хочу сделать несколько таблиц простых или сложных объединений. Я пробовал много разных способов. Я прошел через каждый существующий поток здесь, на SO относительно объединений в репозитории, но ни один из них не помогает в том, что мне нужно.
Вот мой класс репозитория.
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;
}
}
Теперь здесь мы имеем два запроса. данные и data1
данных просто запрашивая одну единственную таблицу с критериями и его возвращающимся результаты, как и ожидалось. Здесь все хорошо.
Но, в data1, также технически возвращение именно конечный результат, что я хочу, но я также необработанный SQL регистратор в месте, чтобы увидеть, что происходит за кулисами, и то, что он делает это для запроса как объявления таблицы и таблицу курортов, используя , выберите * эквивалентный оператор, а затем, когда результаты возвращаются из обеих таблиц, он применяет соединения в памяти и возвращает результат после фильтрации через предложение where. Таким образом, в основном, его сканирование занимает около 100000 строк, хотя я и присоединяюсь, и где предложение находится на месте и в конце концов, оно возвращает одну строку с идентификатором объявления 413.
Одна вещь, чтобы записать то, что я звоню Query метод класса репозитория при выполнении объединения для data1 который возвращающая IEnumerable. Я не смог изменить его на IQueryable, потому что тогда возникло исключение из-за того, что какое-то сообщение, подобное запросу linq, ссылается на разные контексты.
Может ли кто-нибудь помочь мне изменить этот код, чтобы я мог применять реальные sql-соединения между репозиториями или если есть способ изменить этот код, добавив средний слой в качестве моста. Мне нужно что-то сделать дальше. Я искал везде, но безрезультатно. Я уверен, что я не первый в мире, у которого могла быть эта проблема. Там определенно были бы другие, имеющие аналогичную проблему, и может быть кто-то, кто нашел реальный путь.
Мои инструменты и технологии заключаются в следующем.
- Visual Studio 2013
- .Net Framework 4.0
- MySQL Database
- MySQL для Visual Studio 1.2.4
- MySQL Connector 6.9.6
- Entity Framework 6
- для Windows 10 Pro x64
Пожалуйста, дайте мне знать, если есть что-то, что я мог пропустить.
Любая помощь была бы принята с благодарностью.
Edit:
Вот 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;
}
}
Большое спасибо за ваш ответ. Я уже упоминал об этом ранее в своем вопросе, что мне известно, что я возвращаю тип IEnumerable вместо IQueryable, то есть немедленный запрос и отложенный. Но это давало мне ошибку в разных контекстах. Из-за этого я не смог пропустить .ToList(). Надеюсь, это объяснит мою проблему. –
Можете ли вы отправить код, связанный с ContextFactory. Вы получаете от него свой DBContext, создает ли он каждый раз этот контекст или кэшируется. –
Добавьте поле GUID в свой ContextFactory и запишите, что на каждом Get, и посмотрите, совпадают ли они. См. Эту ссылку для получения дополнительных методов тестирования http://www.ladislavmrnka.com/2011/03/unity-build-in-lifetime-managers/ –