I have a .Net Solution with multiple projects for the layers which are as follows.
Business (solution folder)
Core (project)
Interfaces (project)
Data (solution folder)
Data (project)
Presentation (solution folder)
AdminPanel (project)
Shared (solution folder)
Common (project)
I have implemented the generic repository pattern with IoC layer using Unity i.e. Microsoft.Practices.Unity. Everything works great except when I want to do multi table simple or complex joins. I have tried many different ways. I have gone through every single existing thread here on SO regarding joins in repository but none of them are helpful in what I need.
Here's my repository class.
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);
}
}
Here's my IoC layer.
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));
}
}
And finally here's my service layer where I'm trying to query some data from the repository layer.
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;
}
}
Now here we have two queries. data and data1
data is simply querying one single table with a criteria and its returning results as expected. All good here.
But, in data1, it is also technically returning exactly the end result that I want BUT I also have a raw SQL logger in place to see what's happening behind the scenes and what it's doing is to query both ads table and resorts table separately using a select * equivalent statement and then when the results return from both tables, it's applying the joins within memory and returning the result after filtering through the where clause. So basically its scanning through around 100000 rows even though I have join and where clause in place and in the end, it returns one single row with ad id 413.
One thing to note down is that I'm calling Query method of the repository class while doing the join for data1 which is returning an IEnumerable. I was not able to change it to IQueryable because then an exception was being thrown having some message like the linq query has references to different contexts.
Can anyone guide me to modify this code so that I can apply real sql joins between the repositories or if there's a way to modify this code by adding a middle layer to act as a bridge. I need something to go forward from this point. I've been looking everywhere but no avail. I'm sure that I'm not the first one ever in this world who could have this problem. There definitely would be others having similar issue and may be someone who have found a real way.
My tools and technologies are as follows.
Visual Studio 2013
.Net framework 4.0
MySQL Database
MySQL for Visual Studio 1.2.4
MySQL Connector 6.9.6
Entity Framework 6
Windows 10 Pro x64
Please let me know if there's anything that I might have missed.
Any help would be greatly appreciated.
Edit:
Here's the ContextFactory class
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;
}
}
Here is the method you have in your repository which you are using in your data1.
public IEnumerable<TEntity> Query() { IQueryable<TEntity> query = DbSet; return query.ToList(); }
Whenever you hit '.ToList()' it will hit the database.
If you want to create the query first and then execute then use 'DbSet' property of your repository.
Related
I have a Blazor App which uses EntityFrameworkCore and a Generic repository.
In my Services I query the repository, but when I try to call .ToListAsync(); - it requires that I add - using Microsoft.EntityFrameworkCore;
This means it will create a dependency to EF. I don't want to bind my service to EF.
.ToListAsync() is an extension method.
What is the best way to isolate the Service from it? I achieved it through an additional wrapper class - AsyncConverter. Is there a better way?
public class AsyncConverter : IAsyncConverter
{
public Task<List<TSource>> ConvertToListAsync<TSource>(IQueryable<TSource> source, CancellationToken cancellationToken = default)
{
return source.ToListAsync(cancellationToken);
}
}
public class EfRepository<TEntity> : IRepository<TEntity>
where TEntity : class
{
public EfRepository(ApplicationDbContext context)
{
this.Context = context ?? throw new ArgumentNullException(nameof(context));
this.DbSet = this.Context.Set<TEntity>();
}
protected DbSet<TEntity> DbSet { get; set; }
protected ApplicationDbContext Context { get; set; }
public virtual IQueryable<TEntity> All() => this.DbSet;
}
public class ItemsDataService : IItemsDataService
{
private readonly IRepository<Item> _itemsRepository;
private readonly IMapper _mapper;
private readonly IAsyncConverter _asyncConverter;
private readonly IStringLocalizer<Errors> _localizer;
public ItemsDataService(IRepository<Item> itemsRepository,
IMapper mapper,
IAsyncConverter asyncConverter,
IStringLocalizer<Errors> localizer = null)
{
_itemsRepository = itemsRepository;
_mapper = mapper;
_asyncConverter = asyncConverter;
_localizer = localizer;
}
public async Task<Response<IEnumerable<ItemNameDto>>> GetItemsNamesAsync(string searchWord, string userId)
{
try
{
searchWord.ThrowIfNull();
userId.ThrowIfNullOrEmpty();
var query = _itemsRepository.All()
.Where(x => x.UserId == userId);
var names = new List<ItemNameDto>();
if (!string.IsNullOrEmpty(searchWord))
{
query = query.Where(x => x.Name.ToLower().Contains(searchWord.ToLower()));
}
var queryProjection = _mapper.ProjectTo<ItemNameDto>(query); **/*.ToListAsync(); - This would add a dependency to EntityFrameworkCore. That it why I introduced AsyncConverter*/**
names = await _asyncConverter.ConvertToListAsync(queryProjection);
var response = ResponseBuilder.Success<IEnumerable<ItemNameDto>>(names);
return response;
}
catch (Exception ex)
{
var response = ResponseBuilder.Failure<IEnumerable<ItemNameDto>>("An error occured while getting items names.");
return response;
}
}
My usual approach is to put all the query-building code in my repositories, and have them return a materialized result. So I can mock the repository layer when unit-testing my services class.
Trying to unit-test the data access layer can be fraught, since it's inherently designed to be an integration layer. Queries that work one way when you are using in-memory objects may have different behavior when they're translated by Entity Framework.
If you prefer building your queries in the service layer, Ian Kemp's approach can work, basically putting your ConvertToListAsync method into the EfRepository class and its interface instead of having a whole new service to inject.
I am writing a new C# application on the top of Prism 6.3 framework using the well-known MVVM design pattern. I am using Unity IoC container to help me manage my dependencies.
I am using Entity Framework Core to interact with the database. However, I don't want to tightly couple my application to Entity Framework Core, so I implemented Repository and UnitOfWork patterns to make it easy for me to swap out the Entity Framework Core implementation if needed.
My repository implementation provides a method called Save() which calls EF Core's SaveChanges() method. The repositories are injected into my business-service so that my business-service expose one method to do a single task. For example, if I want to create a new order, I would call the Create(orderViewModel) method which internally calls the Add() and the Save() method on the OrderRepository.
Additionally, the UnitOfWork provides Save(), BeginTransaction(), Commit() and Rollback() methods which allow me control the transaction behavior. In another words it will give me the flexibility to either commit or rollback the SQL transaction when needed.
To explain my use case better, here is an example of how I would add new order to my database directly using the business-service without transaction or unit-of-work.
OrdersService.Create(orderViewModel); // this will call the `Add` and the `Save()` methods on the OrderRepository;
Here is another example which demonstrate how I would add a new order and order-items to my database using the business-services while using unit-of-work to start transaction and control the transaction.
using(var transaction = UnitOfWork.BeginTransaction())
{
try
{
var order = OrdersService.Create(orderViewModel);
OrdersService.CreateRange(order.Id, orderItemsViewModel);
transaction.Commit();
}
catch(Exception e)
{
Log.Add(e);
transaction.RollBack();
}
}
In the second example above, even-though the OrdersService.Save and OrdersService.SaveRange each call the SaveChanges() method the data are not committed to the database since I am wrapping them with a transaction.
Question: what LifeTimeManager should I register the DbContext, IUnitOfWork and each of my repositories with?
In a web environment, I would register everything using PerRequestLifetimeManager then during the request I am reusing the same DbContext and everything works fine and the DbContext is disposed at the end of the http request. But not sure how to register everything in a WPF application where I can still use transaction to control everything while allowing the repository to call the SaveChanges()
If needed here is my EntityRepository implementation
public class EntityRepository<TEntity, TKeyType> : IRepository<TEntity, TKeyType>
where TEntity : class
where TKeyType : struct
{
protected readonly DbContext Context;
protected readonly DbSet<TEntity> DbSet;
public EntityRepository(DbContext context)
{
Context = context;
DbSet = context.Set<TEntity>();
}
public TEntity Get(TKeyType id)
{
return DbSet.Find(id);
}
public IEnumerable<TEntity> GetAll()
{
return DbSet.ToList();
}
public bool Any(Expression<Func<TEntity, bool>> predicate)
{
return DbSet.Any(predicate);
}
public IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return DbSet.Where(predicate);
}
public TEntity SingleOrDefault(Expression<Func<TEntity, bool>> predicate)
{
return DbSet.SingleOrDefault(predicate);
}
public virtual TEntity Add(TEntity entity)
{
var record = Context.Add(entity);
record.State = EntityState.Added;
return entity;
}
public virtual IEnumerable<TEntity> AddRange(IEnumerable<TEntity> entities)
{
Context.AddRange(entities);
return entities;
}
public void Remove(TEntity entity)
{
Context.Remove(entity).State = EntityState.Deleted;
}
public void RemoveRange(IEnumerable<TEntity> entities)
{
Context.RemoveRange(entities);
}
public void Update(TEntity entity)
{
DbSet.Attach(entity);
var record = Context.Entry(entity);
record.State = EntityState.Modified;
}
public IQueryable<TEntity> Query()
{
return DbSet;
}
public void Save()
{
Context.SaveChanges();
}
}
And here is my unit of work implementation
public sealed class UnitOfWork : IUnitOfWork
{
private bool IsDisposed = false;
private readonly DbContext Context;
public IOrderRepository Orders { get; private set; }
public IOrderItemRepository OrderItems { get; private set; }
public UnitOfWork(DbContext context)
{
Context = context;
Orders = new OrderRepository(context);
OrderItems = new OrderItemRepository(context);
}
public int Save()
{
Context.SaveChanges();
return 0;
}
public void Dispose()
{
Dispose(true);
}
public IDatabaseTransaction BeginTransaction()
{
return new EntityDatabaseTransaction(Context);
}
private void Dispose(bool disposing)
{
if (IsDisposed)
{
return;
}
if (disposing)
{
Context.Dispose();
}
IsDisposed = true;
}
}
Transient (an instance per view) lifetime would be the way to go if your DI doesn't support scoping, but then you would need to abstract away your DbContext being passed through into the repo's and unitOfWork, otherwise new instances of the DbContext will be passed in there. On construction of the page, a new instance is created, and on moving away from that view, that DBContext should be disposed of. UnitOfWork would follow the same path as you wouldn't want a UnitOfWork spanning multiple instances of a DBContext.
See http://blogs.microsoft.co.il/gilf/2010/02/07/entity-framework-context-lifetime-best-practices/. Otherwise, if your DI has the concept of container hierarchies, and you're able to create a container scope per view, then a singleton would work in this instance and you wouldn't need any abstractions mentioned above and would be quite a bit easier to work with.
I am working on ASP.NET MVC 4 project from scratch. I decided to start with the data access layer using Entity Framework 5 and Code First workflow. The company where I used to work was using very good implementation (in my opinion) of the Repository pattern including Repositories, Services, Abstract Factory for the Repositories and the Services and Unity for DI. I tried to redo it but it's just too complicated for me and will cost me a lot of time to replicate what I've been using there, so I decided to do some research and go with something lighter.
So I decided to use GenericRepository and UnitOfWork - far from what was the initial plan, but that was the implementation that was showing in most of my searches. So I did a very basic implementation (Just to the point where I'm sure I know what's going on, maybe even below my abilities to comprehend) and actually I think for this exact project it may be just enough but what I want is to be able to call additional custom methods on the different entities.
I think this gets a lot from the idea of generic repository, but if I try to go with some other implementation it's getting exponentially harder, so I wonder if there's a way to add this to my implementation without hurting too much the idea behind the generic Repository.
What I have now is the GenericRepository class :
public class GenericRepository<TEntity> where TEntity : class
{
internal DBContext context;
internal DbSet<TEntity> dbSet;
public GenericRepository(DBContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
public virtual IEnumerable<TEntity> Get()
{
IQueryable<TEntity> query = dbSet;
return query.ToList();
}
//just the standard implementation
and my UnitOfWork class :
public class UnitOfWork : IDisposable
{
private DBContext context = new DBContext();
private CustomerRepository customerRepository;
public CustomerRepository CustomerRepository
{
get
{
if (this.customerRepository == null)
this.customerRepository = new CustomerRepository(context);
return customerRepository;
}
}
private GenericRepository<Order> orderRepository;
public GenericRepository<Order> orderRepository
{
get
{
So as you may see my Order entity is using the GenericRepository but I made a test class CustomerRepository to use for my Customer entity.
For now this class CustomerRepository looks like this :
public class CustomerRepository : GenericRepository<Customer>
{
public CustomerRepository(DBContext context) : base(context) { }
}
and the idea is to add the methods that are explicitly for the Customer entity here. I'm not sure if this is correct, especially the way I call the constructor. However, what is the natural way to add those specific methods for the different entities? I don't mind to even take a step back to implement it better but I don't want to rush it cause I tried and at the moment the whole concept is too complicated for me and I want to be sure that I understand the things that I use in my code.
I think you are on the right track. Here is the generic repository that I use:
public interface IRepository<TEntity>
where TEntity : class
{
IQueryable<TEntity> GetAll();
IQueryable<TEntity> GetBy(Expression<Func<TEntity, bool>> predicate);
TEntity GetById(long id);
void Add(TEntity entity);
void Update(TEntity entity);
void Delete(TEntity entity);
}
public class Repository<TEntity> : IRepository<TEntity>
where TEntity : class
{
protected readonly DbEntities Context;
protected readonly DbSet<TEntity> Set;
public Repository()
{
Context = new DbEntities();
Set = Context.Set<TEntity>();
}
public virtual IQueryable<TEntity> GetAll()
{
return Set;
}
public virtual IQueryable<TEntity> GetBy(Expression<Func<TEntity, bool>> predicate)
{
return Set.Where(predicate);
}
public virtual TEntity GetById(long id)
{
return Set.Find(id);
}
public virtual void Add(TEntity entity)
{
Set.Add(entity);
Context.SaveChanges();
}
public virtual void Update(TEntity entity)
{
Set.Attach(entity);
Context.Entry(entity).State = EntityState.Modified;
Context.SaveChanges();
}
public virtual void Delete(TEntity entity)
{
Set.Remove(entity);
Context.SaveChanges();
}
}
// And assuming User is a data object with an Id property:
public interface IUserSpecificRepository
{
List<User> GetById(long id)
}
public class UserSpecificRepository : IUserSpecificRepository, Repository<User>
{
public virtual List<User> GetById(long id)
{
return GetBy(x => x.Id = id).ToList();
}
}
Notice that GetAll() and GetBy() return a queryable. This is to allow control of when the query expression gets converted to SQL and hits the database. Usually a call to ToList() will cause this. You can then inherit from this and any custom methods can make use of these two starter methods.
Also, As a general rule of thumb, you should never do a GetAll().ToList() like you have now. If you have a zilion records you will run into problems. It is also a performance issue if you are filtering down to a few records. GetAll().ToList().Where(x => x.Id = 1) basically gets all zillion records from the db into memory, then filters it down to one. You should instead do this GetAll().Where(x => x.Id = 1).ToList().
Hope this helps you along!
Can Please sugget Best /Efficient way to perform CRUD Opration in MVC application.
I am using Entity Framework in my project for Database access.
ex: EDMX: SupportContext ;
Here i performed the CRUD Operation using SupportContext [.edmx}. which processed more quickly than repository. and also i can retrive data's from more than one context object[table's] in a LinQ query.
ex:
using (SupportContext support =new SupportContext())
{
var=(from t1 in support.table1
from t2 in support.table 2
where t1.id==t2.id
select t1).ToList();
}
I have used Generic Repository Pattern for instead of direct access with EDMx. it act layer between my code logic and EDMX file. find the generic example.
code:
public interface IRepository<T> : IDisposable where T : class
{
IEnumerable<T> Find(Expression<Func<T, bool>> predicate);
void Add(T entity);
void SaveChanges();
}
public class GenericRepository<T> : IRepository<T> where T : class
{
private ObjectContext _context;
private IObjectSet<T> _objectSet;
public GenericRepository(ObjectContext context)
{
_context = context;
_objectSet = _context.CreateObjectSet<T>();
}
public IEnumerable<T> Find(Expression<Func<T, bool>> predicate)
{
return _objectSet.Where<T>(predicate);
}
public void Add(T entity)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
_objectSet.AddObject(entity);
}
public void SaveChanges()
{
_context.SaveChanges();
}
}
Here i have used this repository for CRUD opration as:
var dat1=new GenericRepository<Table1>(new SupportContext());
var dat2=new GenericRepository<Table2>(new SupportContext());
Here i want to retrieve the records in both dat1,dat2 repository while it Id filed is equal. But I can't able to retrieve in combine way. but do it in single way.
var=dat1.Find(t=>t.id==3).toList();
Can you please suggest how to retrieve data 's from combined repository AND also suggest which method is best way to Access data ?
I am receiving the following error:
System.InvalidOperationException was unhandled Message=The type
'Judge' is not attributed with EdmEntityTypeAttribute but is contained
in an assembly attributed with EdmSchemaAttribute. POCO entities that
do not use EdmEntityTypeAttribute cannot be contained in the same
assembly as non-POCO entities that use EdmEntityTypeAttribute.
Source=EntityFramework StackTrace:
at System.Data.Entity.Internal.InternalContext.UpdateEntitySetMappingsForType(Type
entityType)
at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type
entityType)....
public class GenericRepository<TEntity> where TEntity : class
{
internal z context;
internal DbSet<TEntity> dbSet;
public GenericRepository(z context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
public GenericRepository()
{
this.context = new z();
this.dbSet = context.Set<TEntity>();
}
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList(); //Getting error here!!
}
}
public virtual TEntity GetByID(object id)
{
return dbSet.Find(id);
}
public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
}
public virtual void Delete(object id)
{
TEntity entityToDelete = dbSet.Find(id);
Delete(entityToDelete);
}
public virtual void Delete(TEntity entityToDelete)
{
if (context.Entry(entityToDelete).State == EntityState.Detached)
{
dbSet.Attach(entityToDelete);
}
dbSet.Remove(entityToDelete);
}
public virtual void Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
context.Entry(entityToUpdate).State = EntityState.Modified;
}
public virtual void Save()
{
context.SaveChanges();
}
}
The weird part is Judge is attributed with the EdmEntityTypeAttribute, because it is automatically generated as part of the DbContext T-4 jazz.
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmEntityTypeAttribute(NamespaceName="standaloneModel", Name="Judge")]
[Serializable()]
[DataContractAttribute(IsReference=true)]
public partial class Judge : EntityObject
{
At one point I did have another class Judge in a different assembly, but I have since renamed it. I have tried cleaning both projects. There should be no other Judge class besides the EF one.
So I can not figure out where this other Judge class is coming from??
Thanks
Figured it out.
When I first started the program I was using an ObjectContext with the .edmx.
Then I read about EF 4.2 and decided to use DbContext.
The problem was my .edmx file was generating classes, as well as the DbContext T-4s.
The solution was to turn off code generation in the .edmx.
So now, only the DbContext T-4s are generating my POCO classes.
Hope this questions helps someone else in the future!
I had a similar problem - it seems that in some cases (for example, when using WCF Data Services 5.2.0), it's a problem to have code-first/DbContext classes in the same assembly as EDMX/model-first/generated classes. For me, moving the DbContext classes into a separate assembly fixed the problem.
Note that I didn't have a problem with code-first + model-first in the same assembly when just accessing the DB. But as soon as I added another layer (WCF Data Services) I ran into the EdmSchemaAttribute error.