I have this situation:
I'm using Nhibernate to map an Entity 'User' that is mapped on a large table in Sql, for perfomance reasons I've created a lightweight entity 'LightweightUser' that has only a small set of properties and is NOT mapped in a hbm file or whatever, I'm using the Nhibernate Projection technique to wire the 'LightweightUser' entity. Both entities are deriving from a simple class 'Entity' that contains an Id property.
The above implementations works fine, sql query is smaller == faster.
But in my Nhibernate SessionFactory I have also injected a Nhibernate Intercerptor.
The OnLoad method of the Interceptor is called when I'm getting a 'User' entity from the NHibernate Dao, but when I get the 'LightweightUser' entity from the NHibernate Dao the Interceptor is not triggered (OnLoad method).
This is probably related to the fact that NHibernate SessionFactory has no knowledge of my 'LightweightUser' entity. Is there a way to inform the Sessionfactory/ Interceptor of the existence of my 'Lightweight' entity? Or is there some other technique to wire Projections to an Interceptor? And yes I can also duplicate the the 'hbm' file for my Lightweight entity, but that seems like bad practice.
[TestFixture]
public class InterceptorTest : NhibernateSessionHelper
{
[Test]
public void GetEntiy()
{
//ok trigger OnLoad in the Interceptor
var user = GetSession().Get<User>(1);
Assert.IsNotNull(user);
}
[Test]
public void GetProjection()
{
var crit = GetSession().CreateCriteria<User>();
crit.Add(Restrictions.Eq("Id", 5));
crit.SetProjection(Projections.ProjectionList()
.Add(Projections.Property("Id"), "Id")
.Add(Projections.Property("Username"), "UserName"));
crit.SetResultTransformer(Transformers.AliasToBean(typeof(LightweightUser)));
//does not trigger the Interceptor
var result = crit.List<LightweightUser>();
Assert.IsNotNull(result.First());
}
}
//Wire the Sessionfactory with Interceptor
private static ISessionFactory CreateSessionFactory()
{
return new Configuration()
.CurrentSessionContext<ThreadStaticSessionContext>()
.SetInterceptor(new MyInterceptor())
.Configure().BuildSessionFactory();
}
public class MyInterceptor: IInterceptor
{
public override bool OnLoad(object entity, object id, object[] state, string[] propertyNames, NHibernate.Type.IType[] types)
{
//do stuff
var s = entity;
return base.OnLoad(entity, id, state, propertyNames, types);
}
Thanks in advance
The Interceptor's method OnLoad is called only in handling of PreLoadEvent. This is raised in two cases: when an entity is loaded from the database, or when the entity is assembled from the cache. Objects created with a projection like yours are not considered being an entity.
If you want to apply the same code for entities and for tuples of LightweightUser you can implement your custom IResultTransformer containing an instance of
Transformers.AliasToBean(typeof(LightweightUser))
and after calling the original transformer methods apply your logic of
//do stuff
If want to know more about interceptor, I encourage you to read a list of my blog entries starting here: http://blog.scooletz.com/2011/02/03/nhibernate-interceptor-magic-tricks-pt-1/
Related
I have two databasecontexts FooContext, BarContext.
For FooContext, when certain entities in the dbSet changes, I need to project these entities to related entities in BarContext. I am looking at the best ways to implement such a job.
My current attempt involves inheriting an interface for the relevant entities, filtering these out in overridden FooContext.SaveChangesAsync() before calling a separate mapper (using dependencyInjection) to update the relevant tables in BarContext
// Baz.cs
public class Baz : IProjectedEntity {
// baz-definition
}
// FooContext.cs
public class FooContext : DbContext {
private IJobSpawner _jobSpawner;
public FooContext(IJobSpawner jobSpawner){
_jobSpawner=jobSpawner;
}
public DbSet<Baz> Baz {get;}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new()) {
ChangeTracker.DetectChanges();
var projections = ChangeTracker
.Entries<IProjectedEntity>()
.Where(e => e.State != EntityState.Unchanged)
.Select(e => e.Entity).ToList();
int changes = await base.SaveChangesAsync(cancellationToken);
await _jobSpawner.ProjectEntities(projections);
return changes;
}
}
Then the implementation of IJobSpawner takes dependency on BarContext and does the necessary mappings.
Is this a bad idea?
What is an alternative way of doing this?
I have also played a bith with the thought of defining an attribute for class properties, such that projections only happen if certain properties is changed
note : The method i have proposed does work, but I feel there should be a better, less cluttered way of achieving the same result. I don't like having to much internal logic in the context
Given a DbContext and a ClientContext (custom session data about the user) is it possible create a DbContext that is "authorised": where only a subset of the rows on each "table" is available?
With an authorised DbContext I'm trying to have a central row-level authorisation implementation.
I've researched it a bit and the only way to filter out a DbSet would be to use something like Queryable.Where but that returns an IQueryable<T> and there doesn't seem to be a way to return a filtered DbSet<T> (except maybe for global queries that you can setup in Startup but they don't have access to injected dependencies like ClientContext).
Is it possible to define DbSet<T> authorisation filters via an injected scoped dependency like ClientContext?
There are model-level query filters: https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-2.0#model-level-query-filters
From the link:
This feature allows LINQ query predicates (a boolean expression
typically passed to the LINQ Where query operator) to be defined
directly on Entity Types in the metadata model (usually in
OnModelCreating). Such filters are automatically applied to any LINQ
queries involving those Entity Types, including Entity Types
referenced indirectly, such as through the use of Include or direct
navigation property references.
Example from the link:
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public int TenantId { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>().HasQueryFilter(
p => !p.IsDeleted
&& p.TenantId == this.TenantId);
}
}
You can use this for simple scenarios. You define an instance property in your DbContext and in OnModelCreating you specify HasQueryFilter on any entity you want to filter. The property is an instance property, so if you have a scoped DbContext, the correct property value from that request would be used, which is handy if you want to filter by something from your UserContext. I have personally never tried this so I don't know how complex it allows your implementation to be, but you can play with it.
I'm not sure about EF and EF core, but we abstract the DbContext away into functional specific 'logic' blocks.
e.g:
class DbContext()
{
public DbSet<PeopleEntity> peoples;
}
class PeopleLogic()
{
DbContext _context;
PeopleLogic(DbContext context)
{
_context = context;
}
IEnumerable GetAllPeoples()
{
// create context,
// apply filters
// return result
}
}
We ofcourse have a base for simple CRUD operations;
public void AddOrUpdate(){
lock (SyncDatabaseWriteObject)
{
try
{
using (var context = CreateContext())
{
//insert the entity and add it to the db context
context.Set<TEntity>().AddOrUpdate((TEntity)entity);
context.SaveChanges();
}
return entity;
}
catch (Exception ex)
{
throw new DatabaseAccessException("Error occured while getting saving.", ex);
}
}
}
And instead of passing the dbcontext around, we pass around logics.
e.g. we seperate the logic for the database and the access to the database into 2 seperate projects, the business layer then only uses the dbAccess layer.
I'm writing a IQueryable<T> extension method and I would like to get the foreign keys of the navigation properties of T. I have figured out that I can get access to them through IModel.FindEntityType(T).GetNavigations(), the question is how to best get access to the IModel while keeping the method as simple as possible.
At the moment the method looks something like this:
public static IQueryable<TQuery> DoMagic<TQuery>(this IQueryable<TQuery> query, IModel model)
{
var entity = model.FindEntityType(entityType);
var navigationProperties = entity.GetNavigations();
...
}
I would love if I wouldn't need to pass IModel as an argument like this:
public static IQueryable<TQuery> DoMagic<TQuery>(this IQueryable<TQuery> query)
{
var entity = Model.FindEntityType(entityType);
var navigationProperties = entity.GetNavigations();
...
}
I have considered adding a IServiceCollection extension method and passing the DbContext and setting IModel as a static property to the IQueryable<T> extension method. The problem with that is that it is limiting me to one DbContext.
I have also looked into the option to add a extension method to DbContextOptionsBuilder but haven't really figured out best way to achieve what I want to do this way.
Maybe there is other ways to get access to the foreign keys? Any help is greatly appreciated!
Edit
From the navigation properties I want to access the ForeignKey-property to know what property is used to resolve the navigation property. Something like:
navigationProperties.ToList().Select(x => x.ForeignKey);
One way of getting the navigation properties:
public static IQueryable<TQuery> DoMagic<TQuery>(this IQueryable<TQuery> query)
{
var navigationProperties = typeof(TQuery).GetProperties()
.Where(p => (typeof(IEnumerable).IsAssignableFrom(p.PropertyType) && p.PropertyType != typeof(string)))
.ToArray();
...
}
UPDATE:
Trying to get DbContext which has the Model property from IQueryable would result in some not pretty code, this link shows how it could be done:
.Net EF Core 2.1 Get DbContext from IQueryable argument
If you are using dependency injection, you could put the instance of DbContext into the container in the scope of the web request or the operation and inject it into the class with the DoMagic method.
Another way could to add the method to the descendant of DbContext class, then the model parametere would not be needed.
I have .Net 4.0 application which use EntityFramework 5.0 to access data from MS SQL database.
I use database first approach. All tableas are mapped to POCO entities, which has additional properties which contains entities which is recieved from web service.
Database:
WH_Product (Id, NomenklatureId, SeriesId, Quantity)
Service have such data:
Nomenklature (Id, Name, Producer...)
Series (Id, Number, Date...)
POCO entity:
Product (Id, NomenklatureId, Nomenklature, SeriesId, Series, Quantity)
I have a problem with Repository realisation. I need to implement lazy loading for Nomenklature and Series properties.
I make ProductProxy class which implements such loading like this:
public class ProductProxy:Product
{
private Nomenklature _nomenklature;
public override Nomenklature Nomenklature
{
get
{
if (_nomenklature==null)
{
_nomenklature = <code for load Nomenklature from webService by base.NomenklatureId>
}
return _nomenklature;
}
}
private Series _series;
public override Series Series
{
get
{
if (_series==null)
{
_series = <code for load Series from webService by base.NomenklatureId>
}
return _series;
}
}
}
Then change Person class to PersonProxy class in DbContext.
public class ProductContext:DbContext
{
...
public DbSet<PersonProxy> Person {get; set;}
...
}
The load method:
public List<Person> GetPersons()
{
using (var ctx = new ProductContext())
{
var persons = ctx.Person.AsEnumerable().Cast<Person>().ToList();
return persons;
}
}
Question:
Is this a better way to realize GetPersons method without AsEnumerable().Cast()?
Is this another way of changing the entity type with the descendant proxy type?
The Proxy pattern is taking advantage of Polymorphism (one of 'The 3 Pillars of OOP'), the consumer of the object think it deals with a Person while in-fact he's dealing with a PersonProxy. that is possible because PersonProxy is a Person because it derives from it.
That's why you can write:
Person person = new PersonProxy();
Then in your case, your GetPersons() method could simply return IEnumerable<Person>, as follows:
public IEnumerable<Person> GetPersons()
{
using (var ctx = new ProductContext())
{
return ctx.Person;
}
}
If you value your entity classes being light, you could approach this differently, without putting loading logic in descendant proxies.
Instead, keep you entity classes simple and have them loaded externally after they're created (materialized).
Ladislav's answer shows how to get to the event that fires after every entity object is created. If you use this approach, subscribe to it with a handler, that distinguishes different entity classes and loads them appropriately - there's a lot of clean ways to do this.
Do note that if lazy loading of these properties is beneficial to you, this approach will probably not be useful.
I still have some confusion with the Repository Pattern. The primary reason why I want to use this pattern is to avoid calling EF 4.1 specific data access operations from the domain. I'd rather call generic CRUD operations from a IRepository interface. This will make testing easier and if I ever have to change the data access framework in the future, I will be able to do so without refactoring a lot of code.
Here is an example of my situation:
I have 3 tables in the database: Group, Person, and GroupPersonMap. GroupPersonMap is a link table and just consists of the Group and Person primary keys. I created an EF model of the 3 tables with VS 2010 designer. EF was smart enough to assume GroupPersonMap is a link table so it doesn't show it in the designer. I want to use my existing domain objects instead of EF's generated classes so I turn off code generation for the model.
My existing classes that matches the EF model are as follows:
public class Group
{
public int GroupId { get; set; }
public string Name { get; set; }
public virtual ICollection<Person> People { get; set; }
}
public class Person
{
public int PersonId {get; set; }
public string FirstName { get; set; }
public virtual ICollection<Group> Groups { get; set; }
}
I have a generic repository interface like so:
public interface IRepository<T> where T: class
{
IQueryable<T> GetAll();
T Add(T entity);
T Update(T entity);
void Delete(T entity);
void Save()
}
and a generic EF repository:
public class EF4Repository<T> : IRepository<T> where T: class
{
public DbContext Context { get; private set; }
private DbSet<T> _dbSet;
public EF4Repository(string connectionString)
{
Context = new DbContext(connectionString);
_dbSet = Context.Set<T>();
}
public EF4Repository(DbContext context)
{
Context = context;
_dbSet = Context.Set<T>();
}
public IQueryable<T> GetAll()
{
// code
}
public T Insert(T entity)
{
// code
}
public T Update(T entity)
{
Context.Entry(entity).State = System.Data.EntityState.Modified;
Context.SaveChanges();
}
public void Delete(T entity)
{
// code
}
public void Save()
{
// code
}
}
Now suppose I just want to map an existing Group to an existing Person. I would have to do something like the following:
EFRepository<Group> groupRepository = new EFRepository<Group>("name=connString");
EFRepository<Person> personRepository = new EFRepository<Person>("name=connString");
var group = groupRepository.GetAll().Where(g => g.GroupId == 5).First();
var person = personRepository.GetAll().Where(p => p.PersonId == 2).First();
group.People.Add(person);
groupRepository.Update(group);
But this doesn't work because EF thinks Person is new, and will try to re-INSERT the Person into the database which will cause a primary key constraint error. I must use DbSet's Attach method to tell EF that the Person already exists in the database so just create a map between Group and Person in the GroupPersonMap table.
So in order to attach Person to the context I must now add an Attach method to my IRepository:
public interface IRepository<T> where T: class
{
// existing methods
T Attach(T entity);
}
To fix the primary key constraint error:
EFRepository<Group> groupRepository = new EFRepository<Group>("name=connString");
EFRepository<Person> personRepository = new EFRepository<Person>(groupRepository.Context);
var group = groupRepository.GetAll().Where(g => g.GroupId == 5).First();
var person = personRepository.GetAll().Where(p => p.PersonId == 2).First();
personRepository.Attach(person);
group.People.Add(person);
groupRepository.Update(group);
Fixed. Now I have to deal with another issue where Group is being UPDATE'd in the database every time I create a Group/Person map. This is because in my EFRepository.Update() method, the entity state is explicitly set to Modified'. I must set the Group's state toUnchangedso theGroup` table doesn't get modified.
To fix this I must add some sort of Update overload to my IRepository that does not update the root entity, or Group, in this case:
public interface IRepository<T> where T: class
{
// existing methods
T Update(T entity, bool updateRootEntity);
}
The EF4 implentation of the Update method would look something like this:
T Update(T entity, bool updateRootEntity)
{
if (updateRootEntity)
Context.Entry(entity).State = System.Data.EntityState.Modified;
else
Context.Entry(entity).State = System.Data.EntityState.Unchanged;
Context.SaveChanges();
}
My question is: Am I approaching this the right way? My Repository is starting to look EF centric as I start to work with EF and the repository pattern. Thanks for reading this long post
The primary reason why I want to use this pattern is to avoid calling
EF 4.1 specific data access operations from the domain. I'd rather
call generic CRUD operations from a IRepository interface. This will
make testing easier
No it will not make your testing easier. You exposed IQueryable so your repository is not unit testable.
if I ever have to change the data access framework in the future, I
will be able to do so without refactoring a lot of code.
No you will have to change a lot of code anyway because you exposed IQueryable and because EF / ORM is leaky abstraction - your upper layer expects some behavior happens magically inside your ORM (for example lazy loading). Also this is one of the most odd reasons to go for repository. Simply choose the right technology now and use it to get the bets of it. If you have to change it later it means either that you did a mistake and chose the wrong one or requirements have changed - in either case it will be a lot of work.
But this doesn't work because EF thinks Person is new, and will try to
re-INSERT the Person into the database which will cause a primary key
constraint error.
Yes because you are using a new context for each repository = that is wrong approach. Repositories must share the context. Your second solution is not correct as well because you put your EF dependency back to the application - repository is exposing the context. This is usually solved by second pattern - unit of work. Unit of work wraps the context and unit of work forms the atomic change set - SaveChanges must be exposed on unit of work to commit changes done by all related repositories.
Now I have an issue with the Group being UPDATE'd in the database
every time I want to create a Group/Person map.
Why do you change the state? You received entity from the repository so until you detached it there is no reason to call Attach and change the state manually. This all should happen automatically on attached entity. Simply call SaveChanges. If you are using detached entities then you must correctly set state for every entity and relation so in such case you will indeed needs some logic or update overloads to handle all scenarios.
Am I approaching this the right way? My Repository is starting to look
EF centric as I start to work with EF and the repository pattern.
I don't think so. First of all you are not using aggregate roots. If you do you would immediately found that generic repository is not suitable for that. Repository for aggregate roots have specific methods per aggregate root to handle working with relations aggregated by the root. Group is not part of Person aggregate but GroupPersonMap should be so your Person repository should have specific methods to handle adding and removing groups from person (but not to create or delete groups themselves). Imo generic repository is redundant layer.