Is there a way to get an instance of the DbContext an entity is being tracked by (if any)?
I found the following suggestion/solution for EF6
Get DbContext from Entity in Entity Framework
public static DbContext GetDbContextFromEntity(object entity)
{
var object_context = GetObjectContextFromEntity( entity );
if ( object_context == null )
return null;
return new DbContext( object_context, dbContextOwnsObjectContext: false );
}
private static ObjectContext GetObjectContextFromEntity(object entity)
{
var field = entity.GetType().GetField("_entityWrapper");
if ( field == null )
return null;
var wrapper = field.GetValue(entity);
var property = wrapper.GetType().GetProperty("Context");
var context = (ObjectContext)property.GetValue(wrapper, null);
return context;
}
Is there a way to get this result in EF Core?
No. EF Core does not have lazy loading yet. If it had, then, a proxy generated from it would eventually have a reference to the DbContext that loaded it. As of now, there is no such reference.
One could use dependency injection on the instance/entity at creation. To allow the owning dbcontext to be retrieved from the entity later.
eg
class Book
{
public readonly DBContext _dbcontext;
public Book(DBContext dbcontext)
{
_dbcontext = dbcontext;
}
}
There is no good way to do this. There seems to be no easy way to inject any code into the process after an entity object is constructed but before it is enumerated through in the calling code.
Subclassing InternalDbSet was something I considered but you can only fix calls to the .Find methods and the IQueryable implementation (the main way you'd use a DbSet) is out of reach.
So the only option I can see left is to not allow access to the DbSet at all but have accessor functions which will set the .Owner (or whatever you want to call it) property for me. This is messy since you would normally have to write a function for every query type you'd want to make, and the caller couldn't use LINQ any more. But we can use generics and callbacks to preserve most of the flexibility though it looks ugly. Here is what I came up with.
I am working on porting and cleaning up a complex system so I am not in a position to really test this yet but the concept is sound. The code may need further tweaking to work as desired. This should not have any penalties with eg pulling down the entire table before processing any records as long as you use EnumerateEntities to enumerate, instead of QueryEntities, but again I have yet to do any real testing on this.
private void InitEntity(Entity entity) {
if (entity == null) {
return;
}
entity.Owner = this;
// Anything you want to happen goes here!
}
private DbSet<Entity> Entities { get; set; }
public IEnumerable<Entity> EnumerateEntities() {
foreach (Entity entity in this.Entities) {
this.InitEntity(entity);
yield return entity;
}
}
public IEnumerable<Entity> EnumerateEntities(Func<DbSet<Entity>, IEnumerable<Entity>> filter) {
IEnumerable<Entity> ret = filter(this.Entities);
foreach (Entity entity in ret) {
this.InitEntity(entity);
yield return entity;
}
}
public T QueryEntities<T>(Func<DbSet<Entity>, T> filter) {
if (filter is Func<DbSet<Entity>, Entity>) {
T ret = filter(this.Entities);
this.InitEntity(ret as Entity);
return ret;
}
if (filter is Func<DbSet<Entity>, IEnumerable<Entity>>) {
IEnumerable<Entity> ret = filter(this.Entities) as IEnumerable<Entity>;
// You should be using EnumerateEntities, this will prefetch all results!!! Can't be avoided, we can't mix yield and no yield in the same function.
return (T)ret.Select(x => {
this.InitEntity(x);
return x;
});
}
return filter(this.Entities);
}
public void QueryEntities(Action<DbSet<Entity>> filter) => filter(this.Entities);
Related
There are a large number of examples on how to use the Generic Repository Pattern over Entitry Framework but none ever go into great detail. More often than not the example they use for accessing data from a webapi is a one to one mapping to the model.
For Eg. Get a single model object _carRepository<Car>.Get(id); or get all cars _carRepository<Car>.GetAll();
The way I have it implemented is in seperate layers like below:
Controller -> Service Layer -> Repository Layer -> DataAccess
I have my service layer using AutoMapper where I map from the model(entity) to a DTO and that gets returned to the controller who returns that.
The issue that I cant get my head around is how do I return an object from the repository layer which isnt of type < T>.
Eg. I want to return some data for a grid lets say a combination of multipe entities like a customers last number of purchases. Am I supposed to pass the Customer, Product, Orders repository into the service and make multiple calls to the database and aggregate? Or I was thinking in the cutomers repository just make a method which returns like a view dto of sorts already aggreating the data where it does a linq query and projects to this dto. The issue I have is that I'm sure my repository shouldnt know about dto's?
Would appreciate anyone who has thoughts on the correct way of doing this?
In short, I don't ever recommend using the Generic Repository (anti)pattern. The trouble is that it ends up being far too restrictive and inefficient. Methods like GetAll() serve only to end up materializing entire tables into memory. They don't accommodate things like projection (essentially what you are looking for) as well as eager loading related data, filtering, sorting, or things like supporting pagination.
The way I recommend looking at repositories is as a one-to-one supplier of data for a Controller in the MVC pattern. If you have a CarController you have a CarRepository to serve all data for that controller. If you break it down to CarController and AddCarController then similarly you can build repositories to serve each of these. These repositories are not Generic in that they don't just serve Car entities, but any and all entities from the DbContext that this controller (or service) needs rather than trying to marry repositories to a specific entity. This gives them only one concern for their existance, so a CarController only needs to worry about primarily one repository, and that repository only has to worry about serving that controller. (Rather than every controller that might want information about a Car)
Regarding using a Service between the controller and repository, I would only suggest this if there is a distinct requirement to provide that separation. For example, if you want to support both an MVC controller and a public facing WebAPI and you want the inputs and outputs provided to these to be 100% consistent. The data transported from Service to Consumer (Controllers, etc.) would be DTOs. This adds the complexity of needing to send details like sorting, pagination, and filtering etc. from the consumer to the service.
Removing the service can make interacting with the repository a lot easier, and you can leverage projection to populate the DTOs/view models actually sent to the view via the controller actions/endpoints. The way I facilitate this is leveraging IQueryable<TEntity> in the repository. This lets the repository handle low level filtering/rules if necessary and lets the consumer (controller) handle determining how it wants to consume the data.
For example if we have a CarController with a CarRepository and CarRepository has a method:
public IQueryable<Car> GetCars(bool includeInactive = false)
{
var query = _context.Cars.AsQueryable();
if (!includeInactive)
query = query.Where(x => x.IsActive);
return query;
}
When it comes time for the controller to request cars from the repository, it has full control over how to consume it:
var cars = await CarRepository.GetCars()
.Where(x => x.Make == make && x.Model == model)
.OrderBy(x => x.ModelYear)
.Select(x => new CarSummary
{
Make = x.Make,
Model = x.Model,
ModelYear = x.ModelYear,
Color = x.Color,
Features = x.Features.Select(...)
}.Skip(pageSize * (pageNumber-1))
.Take(pageSize)
.ToListAsync();
The controller has full control over how the data should come back including whether to run an async query or synchronous one all without adding any complexity to the repository. So here we can project (Select, or leverage Automapper and ProjectTo) to get whatever data we need from the entities and their related data. This leads to building far faster and memory/network efficient queries because the projections only worry about the data the end consumer actually needs. When we want to load an entity and its relations to perform an update, we can fetch the entity and eager load the related data we need to inspect/validate/update. Eager loading data is faster than lazy loading, but both use a fair bit of memory so we don't want to be serializing and transmitting entire object graphs. However, when doing an update we are typically only dealing with a single top-level object at a time.
The question then becomes "Why use a repository then?" The repository provides a nice abstraction for unit testing, and it can help standardize core rules you want in the system such as Active/Inactive (soft delete) and things like authorization in systems using multi-tenancy or otherwise having distinct access controls for the current user as to what data they should ever be able to see. If you don't have either of these requirements, then there isn't really much point to using a repository at all. The DbContext and it's DbSets essentially serve that role.
If you do have plans for multiple consumers, then the repository remains the same but the above consuming code happens in the Service, returning the DTO/ViewModel. This means that your controller will need to package and transmit standardized requests to the service or parameters covering whether it expects any sorting, pagination, etc. Again, I wouldn't recommend taking on that overhead and complexity unless there is a very real requirement justifying it. It just serves to make the code harder to work with.
This may not be complete answer but few things while working with Generic Repository.
Assumption I made that your generic repository return type of T and T is the entity.
Following is not exact interface but based on your question.
public class IRepository<T>
{
T Get(int Id);
IEnumrable<T> GetAll();
}
So most of the scenario above will suffice the thing.
Now let's assume that you have requirement that will only return specific attribute of T or partial of T.
Generic repository implementation never say that you don't need any extra or extended repository.
So you can do like this.
public interface ICarRepository : IRepository<Car>
{
IList<int> GetIds();
IList<string> GetAllModels();
bool IsActiveInProduction(int carId);
}
Implement above just like you have implemented IRepository for EF core. Inject this in your services and Use ICarRepository instead of IRepository
for your development.
Note: This is personal preference but for large project GenericRepository become issue sometime or may not completely fit but Repository is useful pattern and it overall depends on how you use it.
Update 1
In Car Repository if you want to return some other DTO but make sure it related to Car Entity otherwise it is not reponsibility of Repo layer.
public interface ICarRepository : IRepository<Car>
{
IList<int> GetIds();
IList<string> GetAllModels();
bool IsActiveInProduction(int carId);
MyDto GetMyDto(int carId); // MyDto is just for explanation.
}
Below is the BaseRespository.cs code that is use that I use as Generic class for entity framework. I inherit this class as parent with my other RepositoryClass.
public class BaseRepository
{
#region Ctor(s)
protected RuhBotEntities RuhBotEntities;
public BaseRepository(RuhBotEntities entities)
{
this.RuhBotEntities = entities;
}
#endregion
#region Protected & Private Members
protected async Task<T> Get<T>(Guid id) where T : class
{
return await this.RuhBotEntities.Set<T>().FindAsync(id);
}
protected IQueryable<T> GetAll<T>(out int totalCount ,bool isDeleted, string searchField = null, string searchTerm = null,
int? pageIndex = null, int? pageSize = null, string sortBy = null, SortOrder? sortOrder = null) where T : class
{
IQueryable<T> entities = this.RuhBotEntities.Set<T>();
var sortByProperty = typeof(T).GetProperty(sortBy);
if (!string.IsNullOrWhiteSpace(sortBy) && sortOrder.HasValue && sortByProperty != null)
{
switch (sortByProperty.PropertyType.FullName)
{
case "System.Int32":
entities = this.RuhBotEntities.Set<T>().Sort<T, int>(sortBy, sortOrder);
break;
case "System.String":
entities = this.RuhBotEntities.Set<T>().Sort<T, string>(sortBy, sortOrder);
break;
}
}
PropertyInfo[] entityProperties = typeof(T).GetProperties();
var propertyList = entityProperties.Where(p => p.PropertyType.FullName == "System.String").Select(p => p.Name).ToList();
if (!string.IsNullOrWhiteSpace(searchTerm) && !string.IsNullOrWhiteSpace(searchField))
{
entities = entities.Where(string.Format("{0}.Contains(#0)", searchField), searchTerm);
}
else if (!string.IsNullOrWhiteSpace(searchTerm) && propertyList.Count > 0)
{
var searchFieldList = new List<string>();
propertyList.ForEach(p => searchFieldList.Add(string.Format("{0}.Contains(#0)", p)));
var propertySearchField = String.Join("||", searchFieldList);
entities = entities.Where(propertySearchField, searchTerm);
}
if (isDeleted)
{
entities = entities.Where(string.Format("{0}.Equals(#0)", "IsDeleted"), false);
}
totalCount = entities.Count();
if (pageIndex.HasValue && pageSize.HasValue)
{
entities = entities.OrderBy(sortBy).Skip(pageIndex.Value * pageSize.Value).Take(pageSize.Value);
}
return entities;
}
protected void Add<T>(T entity) where T : class
{
this.RuhBotEntities.Set<T>().Add(entity);
}
public async Task<T> AddIfNotExists<T>(T entity, Expression<Func<T, bool>> predicate = null) where T : class, new()
{
var dbSet = this.RuhBotEntities.Set<T>();
var exists = predicate != null ? dbSet.Any(predicate) : dbSet.Any();
return exists ? await dbSet.FirstOrDefaultAsync(predicate) : dbSet.Add(entity);
}
protected void Update<T>(T entity, params string[] fieldsExcludedForUpdation) where T : class
{
var dbEntityEntry = this.AttachEntity(entity);
var createdOn = dbEntityEntry.Entity.GetType().GetProperty("CreatedOn");
var createdBy = dbEntityEntry.Entity.GetType().GetProperty("CreatedBy");
if (createdOn != null)
{
dbEntityEntry.Property("CreatedOn").IsModified = false;
}
if (createdBy != null)
{
dbEntityEntry.Property("CreatedBy").IsModified = false;
}
if (fieldsExcludedForUpdation != null && fieldsExcludedForUpdation.Length > 0)
{
foreach (var field in fieldsExcludedForUpdation)
{
dbEntityEntry.Property(field).IsModified = false;
}
}
dbEntityEntry.State = EntityState.Modified;
}
protected void Delete<T>(Guid id) where T : class
{
var entity = this.RuhBotEntities.Set<T>().Find(id);
this.RuhBotEntities.Set<T>().Remove(entity);
}
protected async Task SaveChanges()
{
await this.RuhBotEntities.SaveChangesAsync();
}
private DbEntityEntry AttachEntity(object entity)
{
var entry = this.RuhBotEntities.Entry(entity);
if (entry.State == EntityState.Detached)
{
var set = this.RuhBotEntities.Set(entity.GetType());
object attachedEntity = set.Find(entity.GetType().GetProperty("Id").GetValue(entity));
if (attachedEntity != null)
{
var attachedEntry = this.RuhBotEntities.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(entity);
return attachedEntry;
}
else
{
entry.State = EntityState.Modified;
}
}
else
{
this.RuhBotEntities.Set(entity.GetType()).Attach(entity);
this.RuhBotEntities.Entry(entity).State = EntityState.Modified;
}
return entry;
}
#endregion
}
So I just get started,
I have an entity which keeps the data from other entities not by a direct relation but with keeping the EntityId and EntityType(Enum).
When I read these records from GraphQL I expect to resolve a field with a resolver as follow,
public class AssignmentResolver
{
public object GetEntity( Assignment assignment, AppDbContext context)
{
if(assignment.EntityType == AssignmentEntityType.PERSON)
{
return context.People.FirstOrDefault(x => x.Id == assignment.EntityId);
}
// And more checks
return null;
}
}
Then I can say
public class AssignmentQueryType: ObjectType<Assignment>
{
protected override void Configure(IObjectTypeDescriptor<Assignment> descriptor)
{
descriptor.Field("entity").ResolveWith<AssignmentResolver>(x => x.GetEntity(default!, default!));
}
}
I wanna know if this is right or is there a better way...
I mean the better way would be using a document database for this but that's not an option for now.
I also maybe instead of putting the EntityType and EntityId can simply set an actual relation to those other entities but I wanna see if this current way is possible.
Well that was fast.
I found my problem. It seems that in the resolver I cannot just return an object because the schema should be clear when being read.
So from the resolver if I return a viewModel which is shared between all those entities then we are good to go.
So the GetEntity code will change to
public EntityViewModel GetEntity( Assignment assignment, [Service] AppDbContext context)
{
if(assignment.EntityType == AssignmentEntityType.PERSON)
{
var entity = context.People.FirstOrDefault(x => x.Id == assignment.EntityId);
return new EntityViewModel(entity);
}
// And more checks
return null;
}
I am doing a big database call for a shopping cart. It includes many relations, all specified with the .Include() method.
Now I only want EF to include what I have specified. When I include a collection, it automatically loads the collection's relations.
So I have a ShoppingCart, the shopping cart has a collection if ShoppingCartProducts and that one has a relation back to ShoppingCart and to Product.
So I want to include product, but not the shopping cart again, so I do:
IQueryable<ShoppingCart> query = DbContext.ShoppingCarts
.Include(p => p.ShoppingCartProducts)
.Include(p => p.ShoppingCartProducts.Select(x => x.Product))
Later I execute a .FirstOrDefault() which executes the query. Debugging through this, it has also included ShoppingCart within each ShoppingCartProducts.
This sounds a bit small, but it is actually this way throughout the application. New architecture turns entity objects into models with different static methods and extensions. Eventually causing an StackoverflowException because it recursively includes it's relations.
So how do I only Include what I have included?
I've turned LazyLoadingEnabled to false, and ProxyCreationEnabled to false. And my collections/reations are not marked with virtual.
Checked these answers:
DBContext lazyloadingenabled set to true still loads related entities by default
It is true about the include on collections, but once a collection is included, that collection will load all other relations (I guess)
Entity Framework with Proxy Creation and Lazy Loading disabled is still loading child objects
Almost same question, yet not an good answer, only an explanation
EF 6 Lazy Loading Disabled but Child Record Loads Anyway
Using detached didn't help.
Edit:
As Evk mentioned, this has something to do with EF automatically filling up the blanks for already known relations. Question is now actually how to turn this off.
Edit 2:
So after an answer from Evk and my own workaround, we learn that these solutions don't solve the big picture. Let me try to explain:
These extensions and ConvertToModel methods are implemented in every repository and calling each other whenever it has a relation to it. The concept is actually great: Just convert to a model if you have the relation, if you have not, don't do anything. Yet because of this EF 'bug' I learn that all relations that are known inserted everywhere.
Here is an example where our solutions don't work. This for the case the code would call ConvertToModel for the ShoppingCart first, then the rest. But of course it could be visa-versa.
ShoppingCartRepository
public static ShoppingCartModel ConvertToModel(ShoppingCart entity)
{
if (entity == null) return null;
ShoppingCartModel model = new ShoppingCartModel
{
Coupons = entity.ShoppingCardCoupons?.SelectShoppingCouponModel(typeof(ShoppingCart)),
Products = entity.ShoppingCartProducts?.SelectShoppingCartProductModel(typeof(ShoppingCart)),
};
return model;
}
ShoppingCartProductRepository
public static IEnumerable<ShoppingCartProductModel> SelectShoppingCartProductModel(this IEnumerable<ShoppingCartProduct> source, Type objSource = null)
{
bool includeRelations = source.GetType() != typeof(DbQuery<ShoppingCartProduct>);
return source.Select(x => new ShoppingCartProductModel
{
ShoppingCart = includeRelations && objSource != typeof(ShoppingCart) ? ShoppingCartRepository.ConvertToModel(x.ShoppingCart) : null,
ShoppingCartCoupons = includeRelations && objSource != typeof(ShoppingCartCoupon) ? x.ShoppingCartCoupons?.SelectShoppingCouponModel(typeof(ShoppingCartProduct)) : null,
});
}
ShoppingCartCouponRepository
public static IEnumerable<ShoppingCartCouponModel> SelectShoppingCouponModel(this IEnumerable<ShoppingCartCoupon> source, Type objSource = null)
{
bool includeRelations = source.GetType() != typeof(DbQuery<ShoppingCartCoupon>);
return source.Select(x => new ShoppingCartCouponModel
{
ShoppingCart = includeRelations && objSource != typeof(ShoppingCart) ? ShoppingCartRepository.ConvertToModel(x.ShoppingCart) : null,
ShoppingCartProduct = includeRelations && objSource != typeof(ShoppingCartProductModel) ? ShoppingCartProductRepository.ConvertToModel(x.ShoppingCartProduct) : null
});
}
When you study it, you will see it can go from ShoppingCart to ShoppingCartProduct to ShoppingCartCoupon back to ShoppingCart.
My current workaround will be to figure out the aggregate roots and choose which one needs which one. But I rather have an elegant solution to solve this. Best would be to prevent EF from loading those known relations, or somehow figure out if a property was loaded that way (reflection?).
As stated in comments, that's default behavior of entity framework and I don't think it can be changed. Instead, you can change your code to prevent stackoverflow exceptions. How to do that nicely is very dependent on your codebase, but I'll provide one sketch. In the sketch above I use other entity names (because I always check if my code samples at least compile before posting them here):
public static partial class Ex {
public static CodeModel ConvertToModel(Code entity) {
if (entity == null) return null;
CodeModel model = new CodeModel();
var map = new Dictionary<object, object>();
map.Add(entity, model);
model.Errors = entity.Errors?.SelectShoppingCartProductModel(map);
return model;
}
public static ErrorModel[] SelectShoppingCartProductModel(this IEnumerable<Error> source, Dictionary<object, object> map = null) {
bool includeRelations = source.GetType() != typeof(DbQuery<Error>); //so it doesn't call other extensions when we are a db query (linq to sql)
return source.Select(x => new ErrorModel {
Code = includeRelations ? (map?.ContainsKey(x.Code) ?? false ? (CodeModel) map[x.Code] : ConvertToModel(x.Code)) : null,
// other such entities might be here, check the map
}).ToArray();
}
}
Another option is to store current model in thread local variable. If you call some ConvertToModel method and this thread local variable is not null - that means this method has been called recursively. Sample:
public static partial class Ex {
private static readonly ThreadLocal<CodeModel> _code = new ThreadLocal<CodeModel>();
public static CodeModel ConvertToModel(Code entity) {
if (entity == null) return null;
if (_code.Value != null)
return _code.Value;
CodeModel model = new CodeModel();
_code.Value = model;
model.Errors = entity.Errors?.SelectShoppingCartProductModel();
// other setters here
_code.Value = null;
return model;
}
public static ErrorModel[] SelectShoppingCartProductModel(this IEnumerable<Error> source) {
bool includeRelations = source.GetType() != typeof(DbQuery<Error>); //so it doesn't call other extensions when we are a db query (linq to sql)
return source.Select(x => new ErrorModel {
Code = includeRelations ? ConvertToModel(x.Code) : null,
}).ToArray();
}
}
If you implement this in all your ConvertToModel methods - there is no need to pass any parameters or change other parts of your code.
This solution checks if the source object type is not equal to the one we are calling ConvertToModel for.
public static ShoppingCartModel ConvertToModel(ShoppingCart entity)
{
if (entity == null) return null;
ShoppingCartModel model = new ShoppingCartModel
{
...
Products = entity.ShoppingCartProducts?.SelectShoppingCartProductModel(typeof(ShoppingCart)),
};
return model;
}
and the SelectShoppingCartProductModel extension:
public static partial class Ex
{
public static IEnumerable<ShoppingCartProductModel> SelectShoppingCartProductModel(this IEnumerable<ShoppingCartProduct> source, Type objSource = null)
{
bool includeRelations = source.GetType() != typeof(DbQuery<ShoppingCartProduct>);//so it doesn't call other extensions when we are a db query (linq to sql)
return source.Select(x => new ShoppingCartProductModel
{
....
ShoppingCart = includeRelations && objSource != typeof(ShoppingCart) ? ShoppingCartRepository.ConvertToModel(x.ShoppingCart) : null,
});
}
}
Yet this probably doesn't solve the entire problem. If you have another entity, let's say AdditionalCosts inside the ShoppingCart, that also has a reference to ShoppingCartProduct, it will still 'spin around'. If someone has a solution for this it would be great!
ShoppingCart -> ConvertToModel(shoppingCart) -> SelectAdditionalCostsModel -> ShoppingCartProduct -> ConvertToModel(shoppingCartProduct) -> ShoppingCart -> ConvertToModel(shoppingCart). And so on..
In my application it is sometimes necessary to save 10,000 or more rows to the database in one operation. I've found that simply iterating and adding each item one at a time can take upwards of half an hour.
However, if I disable AutoDetectChangesEnabled it takes ~ 5 seconds (which is exactly what I want)
I'm trying to make an extension method called "AddRange" to DbSet which will disable AutoDetectChangesEnabled and then re-enable it upon completion.
public static void AddRange<TEntity>(this DbSet<TEntity> set, DbContext con, IEnumerable<TEntity> items) where TEntity : class
{
// Disable auto detect changes for speed
var detectChanges = con.Configuration.AutoDetectChangesEnabled;
try
{
con.Configuration.AutoDetectChangesEnabled = false;
foreach (var item in items)
{
set.Add(item);
}
}
finally
{
con.Configuration.AutoDetectChangesEnabled = detectChanges;
}
}
So, my question is: Is there a way to get the DbContext from a DbSet? I don't like making it a parameter - It feels like it should be unnecessary.
With Entity Framework Core (tested with Version 2.1) you can get the current context using
// DbSet<MyModel> myDbSet
var context = myDbSet.GetService<ICurrentDbContext>().Context;
How to get a DbContext from a DbSet in EntityFramework Core 2.0
Yes, you can get the DbContext from a DbSet<TEntity>, but the solution is reflection heavy. I have provided an example of how to do this below.
I tested the following code and it was able to successfully retrieve the DbContext instance from which the DbSet was generated. Please note that, although it does answer your question, there is almost certainly a better solution to your problem.
public static class HackyDbSetGetContextTrick
{
public static DbContext GetContext<TEntity>(this DbSet<TEntity> dbSet)
where TEntity: class
{
object internalSet = dbSet
.GetType()
.GetField("_internalSet",BindingFlags.NonPublic|BindingFlags.Instance)
.GetValue(dbSet);
object internalContext = internalSet
.GetType()
.BaseType
.GetField("_internalContext",BindingFlags.NonPublic|BindingFlags.Instance)
.GetValue(internalSet);
return (DbContext)internalContext
.GetType()
.GetProperty("Owner",BindingFlags.Instance|BindingFlags.Public)
.GetValue(internalContext,null);
}
}
Example usage:
using(var originalContextReference = new MyContext())
{
DbSet<MyObject> set = originalContextReference.Set<MyObject>();
DbContext retrievedContextReference = set.GetContext();
Debug.Assert(ReferenceEquals(retrievedContextReference,originalContextReference));
}
Explanation:
According to Reflector, DbSet<TEntity> has a private field _internalSet of type InternalSet<TEntity>. The type is internal to the EntityFramework dll. It inherits from InternalQuery<TElement> (where TEntity : TElement). InternalQuery<TElement> is also internal to the EntityFramework dll. It has a private field _internalContext of type InternalContext. InternalContext is also internal to EntityFramework. However, InternalContext exposes a public DbContext property called Owner. So, if you have a DbSet<TEntity>, you can get a reference to the DbContext owner, by accessing each of those properties reflectively and casting the final result to DbContext.
Update from #LoneyPixel
In EF7 there is a private field _context directly in the class the implements DbSet. It's not hard to expose this field publicly
Why are you doing this on the DbSet? Try doing it on the DbContext instead:
public static void AddRangeFast<T>(this DbContext context, IEnumerable<T> items) where T : class
{
var detectChanges = context.Configuration.AutoDetectChangesEnabled;
try
{
context.Configuration.AutoDetectChangesEnabled = false;
var set = context.Set<T>();
foreach (var item in items)
{
set.Add(item);
}
}
finally
{
context.Configuration.AutoDetectChangesEnabled = detectChanges;
}
}
Then using it is as simple as:
using (var db = new MyContext())
{
// slow add
db.MyObjects.Add(new MyObject { MyProperty = "My Value 1" });
// fast add
db.AddRangeFast(new[] {
new MyObject { MyProperty = "My Value 2" },
new MyObject { MyProperty = "My Value 3" },
});
db.SaveChanges();
}
maybe you could create a helper that disabled this for you and then just call the helper from within the AddRange method
sorry my bad English.
In company where I work we are migrating for Entity Framework Code First.
But problems happen when i create two instance of repository
RepositoryGeneric<Anuncio> repAnuncio1 = new RepositoryGeneric<Anuncio>();
Anuncio anuncio1 = repAnuncio1.find(1)
Anuncio anuncio2 = new Anuncio (1,20,"any thing");
RepositoryGeneric<Anuncio> repAnuncio2 = new RepositoryGeneric<Anuncio>();
repAnuncio2.salvar(anuncio2); //ok work
repAnuncio2.salvar(anuncio1); // error the anuncio1 is atach in repAnuncio1
it is only exemple, but in real application i need several repository interacting.
else, we application use DataContext:
public static void Save(Entity entity)
{
if (entity != null)
{
SqlServer sql = new SqlServer();
Type tipoEntidade = entity.GetType();
PropertyInfo[] propriedades = tipoEntidade.GetProperties();
foreach (PropertyInfo propriedade in propriedades)
{
if (propriedade.PropertyType.IsPublic && propriedade.CanWrite && propriedade.PropertyType.Namespace == "System")
{
object valor = propriedade.GetValue(entidade, null);
if (valor != null && propriedade.Name != "ID" && propriedade.Name != "Excluido")
sql.AdicionarParametro("#" + propriedade.Name, valor);
}
}
but in Entity Framework i could not implement.
else:
public int Save(Anuncio anuncio)
{
if (anuncio.Id != 0)
// ctx.Anuncio.Attach(anuncio);
//attach Error when othe repository get
//else i need...
{
BancoContext ctx = new BancoContext (); //my Dbcontext
AnuncioAxiliar = ctx.Anuncio.FirstOrDefault(x => x.Id == entidade.Id); //Entity help
AnuncioAxiliar.Nome = anuncio.Nome;
AnuncioAxiliar.Cliente= anuncio.Cliente;
AnuncioAxiliar.Contrato= anuncio.Contrato;
ctx.SaveChanges(); //vai salvar o AnuncioAxiliar
}
else
ctx.Set(entidade.GetType()).Add(entidade);
return ctx.SaveChanges();
}
but so i need one Save method for each classe Poco
Any Idea?
That is common problem. You cannot pass entity loaded from one context to another context. You should share context among your repositories when working in one unit of work (logical transaction). It is also enough to use one repository instance per type in the unit of work.
If you want to pass entity from one context to other context you must first detach it from the first context by calling:
ctx.Entry(entity).State = EntityState.Detached;
Be aware that if entity has any loaded relations these relations will be lost / broken when you detach it from the context.
You need to share the EF db context between your repositories. When you retrieve data from the database via EF, the object that gets returned by EF is attached to a specific instance of a db context for change tracking. If you attempt to perform any operations on that entity in another database context, EF will throw an exception as the database context can see it's already attached to another context.
Note that if this is a web application, your db context should only be shared between repositories in the same web request only. If they are shared between all repositories for all web requests, conflicts will occur.
change the constructor of your RepositoryGeneric to accept your data context as a parameter. Then use the same context for all the repositories. You should probably use dependency injection to achieve this.
Something like below
public class RepositoryGeneric<TEntity>
{
public RepositoryGeneric(DomainContext domainContext)
{
DomainContext = domainContext;
}
protected DomainContext DomainContext { get; private set; }
protected virtual IDbSet<TEntity> DbSet
{
get { return DomainContext.Set<TEntity>(); }
}
public virtual TEntity GetByKey(params object[] keys)
{
return DbSet.Find(keys);
}
}