I am writing a generic repository for entity framework and am confused as to what the difference between these calls are:
ObjectContext.CreateObjectSet<T>
ObjectContext.CreateQuery<T>
DbContext.Set<T>
I want a generic repository that both supports context generated from .edmx files as well as code first DbContext, so I've got this:
public abstract class EntityRepository<TClass>
where TClass : class, new()
{
//private readonly TContext _context;
private readonly ObjectSet<TClass> _objectSet;
protected EntityRepository(IObjectContextAdapter context)
{
_objectSet = context.ObjectContext.CreateObjectSet<TClass>();
}
protected EntityRepository(ObjectContext context)
{
_objectSet = context.CreateObjectSet<TClass>();
}
public ObjectSet<TClass> Query()
{
return _objectSet;
}
}
In the examples I have seen online I've seen all 3 used, what is the actual differences between them? Is one better performance wise? I know you can write LINQ queries against the contexts using all 3 methods.
The CreateObjectSet<T> returns you ObjectSet<T> that's basically collection of T objects, with ability to add, remove, ... object from this collections resulting later to inserts, deletes, ... You can also use it for querying. It's like a top level root for given entity.
The CreateQuery<T> gives you ObjectQuery<T>, which can be viewed like IEnumerable<T> (it's also IQueryable<T>). This object is like subset of ObjectSet<T> (some conditions etc.), but you can't add items to it and so on.
And finally the Set<T> returns DbSet<T> that's simplified version of first method/object for Code First. It's easier to, for instance, to use these objects (or better to say interfaces; IDbSet<T>) for i.e. unit testing etc. Similar to what ObjectContext and DbContext is.
In addition to what Jiri already explained, there is an overload of CreateObjectSet that takes no arguments. This overload will automatically assume that it has to return the ObjectSet for the only EntitySet associated with TEntity. It will throw if the model has MEST (multiple-entity-sets-per-type). All overloads of CreateQuery need an Entity SQL string to bootstrap the query (note that the name of an EntitySet is a valid Entity SQL query).
Related
I am working on a .net core project where the requirement is to maintain an SQLite DB and an SQL Server DB simultaneously. I created two DbContext files SqlServerContext and SqliteContext and separate migration folders for them. These files are derived from a WorkerContext file that's derived from DbContext. The migration is working properly, as tables are created in both databases. But I could not make simultaneous data operation work.
This is the IKeyboardMouseActivityRepository. There are separate parts for using SqliteContext and SqlServerContext. I have to comment out one part when using the other. So I can do data entry in one DB at a time now.
public interface IKeyboardMouseActivityRepository :
IRepository<KeyboardMouseActivity, Guid, SqlServerContext>
// IRepository<KeyboardMouseActivity, Guid, SqliteContext>
{
}
public class KeyboardMouseActivityRepository :
IKeyboardMouseActivityRepository,
Repository<KeyboardMouseActivity, Guid, SqlServerContext>
// Repository<KeyboardMouseActivity, Guid, SqliteContext>
{
public KeyboardMouseActivityRepository(SqlServerContext dbContext)
: base(dbContext)
{
}
// public KeyboardMouseActivityRepository(SqliteContext dbContext)
// : base(dbContext)
// {
// }
}
This is the main Repository class.
public abstract class Repository<TEntity, TKey, TContext>
: IRepository<TEntity, TKey, TContext>
where TEntity : class, IEntity<TKey>
where TContext : DbContext
{
protected TContext _dbContext;
protected DbSet<TEntity> _dbSet;
public Repository(TContext context)
{
_dbContext = context;
_dbSet = _dbContext.Set<TEntity>();
}
// other methods such as Add, Remove etc.
}
My understanding is that since the context parameter is specified in KeyboardMouseActivityRepository, it only works for that specified context. How can I modify it so it works for both DbContext files and I can do data operation in both DB at the same time?
The repository you have defined is typed per-DbContext. If you want to have a repository that can update two known DbContext implementations then you can back off the Generic approach for the DbContexts and implement the repository to accept one of each in the constructor:
public abstract class Repository<TEntity, TKey>
: IRepository<TEntity, TKey>
where TEntity : class, IEntity<TKey>
{
protected SqlAppDbContext _sqlContext;
protected SqlLiteAppDbContext _sqlLiteContext;
protected DbSet<TEntity> _sqlDbSet;
protected DbSet<TEntity> _sqlLiteDbSet;
public Repository(SqlAppDbContext sqlContext, SqlLiteAppDbContext sqlLiteContext)
{
_sqlContext = sqlContext ?? throw new ArgumentNullException("sqlContext");
_sqlLiteContext = sqlLiteContext ?? throw new ArgumentNullException("sqlLiteContext");
_sqlDbSet = _sqlContext.Set<TEntity>();
_sqlLiteDbSet = _sqlLiteContext.Set<TEntity>();
}
// other methods such as Add, Remove etc.
}
Note that you will want to investigate and implement something like TransactionScope to help ensure that operations done via the repository are mutually committed or rolled back. For instance if you have code that attempts to update data in both DbSets and SaveChanges, if one succeeds and the other fails for any reason, usually the expectation would be they both roll back. Reads I expect would prioritize one DbSet over the other, but expect if you were to want to support something like a fail-over or situational load from one server or the other you will run into issues if it is at all possible that entities fetched from one DbContext are ever married up with entities fetched from the other. (entities loaded by _sqlContext cannot be associated with entities loaded by _sqlLiteContext) When updating entities and associating them via navigation properties you will be loading everything twice or playing a very dangerously error prone game of detaching and reattaching entities betewen DbContexts.
I would advise against using a Generic Repository pattern /w EF. This will paint you into various corners that will limit many of the capabilities that EF can provide for optimizing queries, working with projections, and performing operations like pagination, filtering, sorting, etc. efficiently without a lot of extra code or introducing pretty complex code into the repository.
Overall I wish you luck with the project, however a requirement and design like this will be a nest of hungry dragons for your time and sanity. :)
I am building web APIs in ASP.NET Core 1.1.
I have a number different databases (for different systems) which have common base schemas for configuration items such as Configuration, Users and groups (about 25 tables in all). I am trying to avoid duplicating the quite extensive EF configuration for the shared part of the model by inheriting from a base class as shown in the diagram.
However, this does not work because of the Entity Framework (EF) requirement to pass DbContextOptions<DerivedRepository> as a parameter to the constructor, where DerivedRepository must match the type of the repository the constructor is called on. The parameter must then be passed down to the base DbContext by calling :base(param).
So when (for example) InvestContext is initialised with DbContextOptions<InvestContext>, it calls base(DbContextOptions<InvestContext>) and EF throws an error because the call to the ConfigurationContext constructor is receiving a parameter of type DbContextOptions<InvestContext> instead of the required type DbContextOptions<ConfigurationContext>. Since the options field on DbContext is defined as
private readonly DbContextOptions _options;
I can't see a way around this.
What is the best way to define the shared model once and use it multiple times? I guess I could create a helper function and call it from every derived context, but it's not nearly as clean or transparent as inheritance.
I would like to bring this post from the OP's GitHub issue to everyone's attention:
I was able to resolve this without a hack by providing a protected constructor that uses DbContextOptions without any type. Making the second constructor protected ensures that it will not get used by DI.
public class MainDbContext : DbContext {
public MainDbContext(DbContextOptions<MainDbContext> options)
: base(options) {
}
protected MainDbContext(DbContextOptions options)
: base(options) {
}
}
public class SubDbContext : MainDbContext {
public SubDbContext (DbContextOptions<SubDbContext> options)
: base(options) {
}
}
OK, I have got this working in a way which still uses the inheritance hierarchy, like this (using InvestContext from above as the example):
As stated, the InvestContext class receives a constructor parameter of type DbContextOptions<InvestContext>, but must pass DbContextOptions<ConfigurationContext> to it's base.
I have written a method which digs the connectionstring out of a DbContextOptions variable, and builds a DbContextOptions instance of the required type. InvestContext uses this method to convert its options parameter to the right type before calling base().
The conversion method looks like this:
protected static DbContextOptions<T> ChangeOptionsType<T>(DbContextOptions options) where T:DbContext
{
var sqlExt = options.Extensions.FirstOrDefault(e => e is SqlServerOptionsExtension);
if (sqlExt == null)
throw (new Exception("Failed to retrieve SQL connection string for base Context"));
return new DbContextOptionsBuilder<T>()
.UseSqlServer(((SqlServerOptionsExtension)sqlExt).ConnectionString)
.Options;
}
and the InvestContext constructor call changes from this:
public InvestContext(DbContextOptions<InvestContext> options):base(options)
to this:
public InvestContext(DbContextOptions<InvestContext> options):base(ChangeOptionsType<ConfigurationContext>(options))
So far both InvestContext and ConfigurationContext work for simple queries, but it seems like a bit of a hack and possibly not something the designers of EF7 had in mind.
I am still concerned that EF is going to get itself in a knot when I try complex queries, updates etc. It appears that this is not a problem, see below)
Edit: I've logged this problem as an issue with the EF7 team here, and a team member has suggested a change to the EF Core core as follows:
"We should update the check to allow TContext to be a type that is derived from the current context type"
This would solve the problem.
After further interaction with that team member (which you can see on the issue) and some digging through the EF Core code, the approach I've outlined above looks safe and the best approach until the suggested change is implemented.
Depending on your requirements you can simply use the non type specific version of DbContextOptions.
Change these:
public ConfigurationContext(DbContextOptions<ConfigurationContext> options):base(options)
public InvestContext(DbContextOptions<InvestContext> options):base(options)
to this:
public ConfigurationContext(DbContextOptions options):base(options)
public InvestContext(DbContextOptions options):base(options)
Then if you create your ConfigurationContext first, the classes that inherit it seem to get the same configuration. It may also depend on the order in which you initialize the different contexts.
Edit:
My working example:
public class QueryContext : DbContext
{
public QueryContext(DbContextOptions options): base(options)
{
}
}
public class CommandContext : QueryContext
{
public CommandContext(DbContextOptions options): base(options)
{
}
}
And in Startup.cs
services.AddDbContext<CommandContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddDbContext<QueryContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
alternatively, in a test class:
var connectionString = "Data Source=MyDatabase;Initial Catalog=MyData;Integrated Security=SSPI;";
var serviceProvider = new ServiceCollection()
.AddDbContext<QueryContext>(options => options.UseSqlServer(connectionString))
.BuildServiceProvider();
_db = serviceProvider.GetService<QueryContext>();
I need guidance on designing data layer for my Web API services. The Web API controllers call the service layer which calls the data layer.
I am planning to use Entity Framework along with Dapper. It might not be a good solution to use both of them together, but I need both. I need EF as it is easier to use and developers in my team are familiar. I need Dapper for performance. So, it will be a mix depending on where the dapper can make significant impact and where we can compromise on being a little late.
When using EF, I wanted to use unit of work with repository for each entity. My repository will be like
public class StudentRepository : IStudentRepository, IDisposable
{
private SchoolContext context;
public StudentRepository(SchoolContext context)
{
this.context = context;
}
public IEnumerable<Student> GetStudents()
{
return context.Students.ToList();
}
}
I took that sample code from http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
So, now I wanted to introduce Dapper.
Approach 1: Initially I thought of having multiple repositories for Dapper and for Entity Framework and I can register the one which I need in my dependency injection container. But in this case, all the methods from IStudentRepository interface needs to be implemented in both the EF and Dapper concrete repository classes (if I could do this in Dapper completely, then I don't need EF at all).
Approach 2 : Then I thought about a more ugly approach and it is like exposing a property of IDbConnection along with the DbContext property (in this case SchoolContext) in the above StudentRepository class.
So the example would be like
public class StudentRepository : IStudentRepository, IDisposable
{
private SchoolContext context;
private IDbConnection Db;
public StudentRepository(SchoolContext context)
{
this.context = context;
this.db = new SqlConnection(ConfigurationManager.ConnectionStrings["conn"].ConnectionString);
}
public IEnumerable<Student> GetStudents()
{
return context.Students.ToList();
}
public IEnumerable<Student> GetStudentsBasedOnSomeComplexCondition()
{
//I can use the db property here and work with dapper in this case.
}
(The inclusion of the IDbConnection property can be done through an abstract class so as not to repeat the instantiation code of this property and to easily change the connection string in case if needed. I am adding it in the same class for simplicity).
Approach 3 : Now, I thought of separating it further which I again think is an ugly way. Along with StudentRepository which has only EF stuff (like the first example), I will have another concrete class called StudentDapperRepository which inherits from StudentRepository.
All the methods in StudentRepository will be changed to virtual. So, I will be using StudentDapperRepository for my actual data layer and this will have the Dapper implementations where needed and where not needed, it will use the base class StudentRepository methods (which is in EF).
I think all my solutions are ugly and adding more complexity and confusion. So, can I have some light into how I can do this.
We have a multi-layered application, where all the repositories are based on a (home-grown) GenericRepository base class (where T is an entity in the model), that exposes methods such as GetContext(), GetObjectSet() and so on. We allow the repositories that inherit from this to access the context, as they need to call Include(), as we are passing the data up through a WCF service, so need to load all related entities eagerly.
All of our entities implement an interface that has an Active bool property, and what we want to do is intercept the execution of a query, and filter on the Active property, so that any query only returns entities where this is set to true.
Can this be done? In Lightswitch, which is built on EF, there is an event you can capture that gets fired right down in the depths of the query execution, and allows you to do this sort of filtering. I can't find anything in EF itself that allows this.
Anyone any ideas? Thanks
In EF 5, Include is an extension method on IQueryable, so you can do this:
var query = dbSet.Where( o => o.IsActive ).Include( ... )
That means, you don't have to return a DbSet<T> from your generic repository - it should be ok to return an IQueryable<T>.
If this meets your requirements, you can add a Where clause to your generic repository method:
partial class GenericRepository<T>
{
public IQueryable<T> Query( bool includeInactive = false )
{
return ctx.Set<T>().Where( o => includeInactive || o.IsActive );
}
}
The best way that I can think of doing that would be to have your repository methods take in an Expression (i.e. Expression<Func<T, bool>> predicate). That way you can do all of your queries in the actual repository itself (and thus not allowing client-side code any way of accessing your data layer logic) to which you can add a Where before you return from the repository method to only grab those that are Active.
An example of this style that I've used is the following:
public IQueryable<T> Grab(Expression<Func<T, bool>> predicate)
{
return DbSet.Where(predicate);
}
Where DbSet is the actual table that you're trying to query. That way you can add .Where(x => x.Active) to the end of it, have it not execute against the database yet (thank you, deferred execution!) and yet still get the exact records that you're looking for.
Let me say, I have come to the conclusion (after a lot of trial) that Repository & Unit of Work when using Entity Framework is just wrong, wrong, wrong and this says why quite well.
But I really hate on those embedded queries. Question is, where can I put them instead if I'm so against a repository, etc? (clean answers only please, examples much appreciated).
I just nuked two projects containing my repositories, unit of work and interfaces with hundreds of files because the payback was nowhere to be seen. I think lots of people, myself included, just jumped on the Repository bandwagon because that's what everybody else was doing but in retrospect, I think it's really a ride to nowhere.
/sigh
Richard
Where do you expect to put them? You have only few choices:
Let them be where they are and use custom extension methods, query views, mapped database views or custom defining queries to define reusable parts
Expose every single query as method on some separate class. The method mustn't expose IQueryable and mustn't accept Expression as parameter = whole query logic must be wrapped in the method. But this will make your class covering related methods much like repository (the only one which can be mocked or faked). This implementation is close to implementation used with stored procedures.
You will do the same as in previous method but instead of placing queries in separate class you will put them as static methods to entity directly. This is much worse testable because static methods cannot be replaced by mocking (it requires more complex testing framework). This is part of active record pattern where each entity is responsible for its loading and saving to database.
Example of custom extension method:
public static IQueryable<TEntity> GetByName(this IQueryalbe<TEntity> query, string name)
where TEntity : IEntityWithName
{
return query.Where(e => e.Name == name);
}
Example of custom class exposing methods:
public class QueryProvider
{
public QueryProvider() {}
public IEnumerable<TEntity> GetByName(IYourContext context, string name)
where TEntity : IEntityWithName
{
return context.CreateObjectSet<TEntity>().Where(e => e.Name == name).ToList();
}
}
Build Reusable, Testable Queries Part 1
This is a blog post I wrote about building reusable queries. Using Extension Methods allows you to build composable queries.
using a pattern like the specification pattern can help you build queries that can be reused or saved (serialized). Further more if you have a double entry system you can execute the same query instance over two different databases.
the following example does not use EF but replace the IEnumerable by an EF context and you get what ou are looking for. parameters are passed in through the constructor.
public class PartialMatchQuery : IModelQuery<string, IEnumerable<string>>
{
private readonly string partial;
public PartialMatchQuery(string partialString)
{
partial = partialString;
}
public IEnumerable<string> Execute(IEnumerable<string> model)
{
return model.Where(s => s.ToLower().Contains(partial));
}
}