How to get a reference to superior object from Entity object - entity-framework

How to get a reference to superior ObjectContext from EntityObject class?

Have a look at the following link:
http://blogs.msdn.com/alexj/archive/2009/06/08/tip-24-how-to-get-the-objectcontext-from-an-entity.aspx
It is like the way that Nix mentioned as an extenxtion to the entity object.

Only way you can do it is via a hack using relationships, and an entity that is not detached. See below.
YourEntity someEntity = null;
RelationshipManager relationshipManager = ((IEntityWithRelationships)someEntity ).RelationshipManager;
IRelatedEnd relatedEnd = relationshipManager.GetAllRelatedEnds().FirstOrDefault();
ObjectQuery getContext = relatedEnd.CreateSourceQuery() as ObjectQuery;
YoutObjectContext c1 = (YourObjectContext)getContext .Context;
Good luck with it. If you use the code above i recommend protecting it with null checks.

Related

How to find all managed attached objects in EntityManager (JPA)

Is there a way to get all objects which are currently attached in the entity manager?
I want to write some monitoring code which will report the number of attached objects and their classes.
Meaning finding all objects which were loaded by previous queries and find operations into the entity manager.
I'm using EclipseLink, so a specific solution is good too.
EclipseLink's JPA interface pretty much wraps its native code such that an EntityManager uses a UnitOfWork session underneath (and the EMF wraps a ServerSession). You need to get at the UnitOfWork if you want to see what entities it is managing.
If using JPA 2.0, you can use the EntityManager unwrap method:
UnitOfWork uow = em.unwrap(UnitOfWork.class);
otherwise, use some casting
UnitOfWork uow = ((EntityManagerImpl)em).getUnitOfWork();
From there, the UnitOfWork has a list of all registered (aka managed) entities. You can use the UOW to directly log what it has using the printRegisteredObjects() method, or obtain it yourself using getCloneMapping().keySet().
You can also see deleted objects by using hasDeletedObjects() and then getDeletedObjects().keySet() if there are any, as and the same for new objects using hasNewObjectsInParentOriginalToClone() and getNewObjectsCloneToOriginal().keySet()
you can use JPA in a lot of ways i am still unaware of, and there is a lot going on under the hood in eclipselink that i still do not fully understand, but it looks like it is possible to see into the persistence context. USE THIS CODE AT YOUR OWN RISK. it is only meant to give you a hint that it is possible to inspect the context. (whether the code is right or wrong i'm posting it because it would have helped me when i was trying to decide whether to use eclipselink. there doesn't seem to be much in the way of documentation about how to do this properly.)
public void saveChanges() {
Date now = new Date();
JpaEntityManager jem = em.unwrap(JpaEntityManager.class);
UnitOfWorkImpl uow = jem.unwrap(UnitOfWorkImpl.class);
// inserts
for (Object entity : uow.getNewObjectsCloneToOriginal().keySet()) {
if (entity instanceof IAuditedEntity) {
IAuditedEntity auditedEntity = (IAuditedEntity) entity;
auditedEntity.setAuditedUserId(this.userId);
auditedEntity.setAuditedAt(now);
auditedEntity.setCreatedAt(now);
}
}
// updates
UnitOfWorkChangeSet uowChangeSet = (UnitOfWorkChangeSet) uow.getUnitOfWorkChangeSet();
if (uowChangeSet != null) {
List<IAuditedEntity> toUpdate = new ArrayList<>();
for(Entry<Object, ObjectChangeSet> entry : uowChangeSet.getCloneToObjectChangeSet().entrySet()) {
if (entry.getValue().hasChanges()) {
if (entry.getKey() instanceof IAuditedEntity) {
toUpdate.add((IAuditedEntity) entry.getKey());
}
}
}
for (IAuditedEntity auditedEntity : toUpdate) {
auditedEntity.setAuditedUserId(this.userId);
auditedEntity.setAuditedAt(now);
}
}
// deletions
Project jpaProject = uow.getProject();
boolean anyAuditedDeletions = false;
for (Object entity : uow.getDeletedObjects().keySet()) {
if (entity instanceof IAuditedEntity) {
anyAuditedDeletions = true;
DeletedEntity deletion = new DeletedEntity();
deletion.setTableName(jpaProject.getClassDescriptor(entity.getClass()).getTableName());
deletion.setEntityId(((IAuditedEntity) entity).getId());
deletion.setAuditedUserId(this.userId);
em.persist(deletion);
}
}
}
You can achieve this by inspecting the entities on MetaModel which can be obtained from any EntityManager.
Example usage:
EntityManager em = // get your EM however...
for(EntityType<?> entityType : em.getMetaModel().getEntities())
{
Class<?> managedClass = entityType.getBindableJavaType();
System.out.println("Managing type: " + managedClass.getCanonicalName());
}
This example will print out all of the class types being managed by the EntityManager. To get all of the actual objects being managed, simply query all objects of that type on the EntityManager.
Update:
As of JPA 2.0 you can cache results that will be managed by javax.persistence.Cache. However, with plain JPA there is no way to actually retrieve the objects stored in the cache, the best you can do is check if a certain object is in the Cache via Cache.contains(Class cls, Object pk):
em.getEntityManagerFactory().getCache().contains(MyData.class, somePK);
However, EclipseLink extends Cache with JpaCache. You can use this to actually get the object from the cache via JpaCache.getObject(Class cls, Object id). This doesn't return a collection or anything, but it's the next best thing.
Unfortunately, if you want to actually access objects in the cache, you will need to manage this yourself.
I dont see such an option in the EntityManager interface. There is only a contains(Object entity) method but you need to pass the conrete objects and they are the checked for existentnce in the PersistenceContext. Also looking at the PersistenceContext interface i dont see such an option.

How to Determine a property is ComplexType?

I want to implement an IsComplexType() method, which check if the given property from an entity is a ComplexType.
After reading Entity Framework's source code, i find it has implemented one in the "Helper" class, but the class is "internal", so i can't use it outside Entity Framework project.
I wonder if there's a public API in Entity Framework which enable me to do this. If not, how can i implement it?
Try this :
var dbContext = new DbContext("ConnectionString");
var complexType = dbContext.Entry(TEntity).ComplexProperty("ProperyName");
if (complexType != null)
{
// This is a Complex Type
}
Hope this help.

Recursively applying MergeOption.NoTracking

I want to get an object from entity framework and return it along with all its related objects to the user. When I set the MergeOption to MergeOption.NoTracking, I do get the first related objects even after the entities/context object get destroyed. But when I try to get the related objects of the related objects, I get an exception saying that the entities object doesn't exists any more! I tried setting the MergeOption on all entities that are retrieved, but that didn't work. Any idea how to solve this problem?! Here is my code:
MyFirstObject myObject;
using (var entities = new MyEntities())
{
entities.MyFirstObject.MergeOption = MergeOption.NoTracking;
entities.MySecondObject.MergeOption = MergeOption.NoTracking;
entities.MyThirdObject.MergeOption = MergeOption.NoTracking;
myObject = entities.MyFirstObject.First();
}
myObject1.MySecondObjects..... // Works fine.
myObject1.MySecondObjects.MyThirdObjects.... // Throw an exception.
MergeOption has no effect on this. MergeOption just tells EF how to handle materialized entities - NoTracking means that context must not track entities for changes. You must use eager loading.
myObject = entities.MyFirstObject
.Include("MySecondObjects.MyThirdObjects")
.First();

Entity Framework 4: Access current datacontext in partial entity class

I want to extend an EF entity in a partial class with methods and properties. I've done this quite often.
But now I would need to combine data from this entity with data from other entities. I would therefore need to able to access the entities objectcontext (if attached) to make these queries.
Is there a way to get the entities objectcontext from within it?
Thanx!
There is no build in way to get current ObjectContext from entity. Entities based on EntityObject class and POCO proxies uses ObjecContext internally but they don't expose it.
Adding such depnedency into your entities is considered as bad design so you should perhaps explain what you are trying to do and we can find other (better) solution.
Even though it is not recommended, and I myself don't use it (as Ladislav stated: bad design), I stumbled upon a solution:
http://blogs.msdn.com/b/alexj/archive/2009/06/08/tip-24-how-to-get-the-objectcontext-from-an-entity.aspx
Extension Method:
public static ObjectContext GetContext(
this IEntityWithRelationships entity
)
{
if (entity == null)
throw new ArgumentNullException("entity");
var relationshipManager = entity.RelationshipManager;
var relatedEnd = relationshipManager.GetAllRelatedEnds()
.FirstOrDefault();
if (relatedEnd == null)
throw new Exception("No relationships found");
var query = relatedEnd.CreateSourceQuery() as ObjectQuery;
if (query == null)
throw new Exception("The Entity is Detached");
return query.Context;
}
within the entity
var myContext = this.GetContext() as MyEntities;

How to save child relationship entities in the Entity Framework

I have an Entity Framework v1 project. I have two entities (Roles and Permissions), which have a many-to-many relationship with each other. I pass in a object to be saved (through a WCF call, I do not create it from a context myself), which has new entries in the many-to-many relationship.
I use "context.ApplyPropertyChanges" to update the record with the new properties. I know that this does not update relationships though. I attempt to either do a ChildCollection.Add(relatedObject); or ChildCollection.Attach(relatedObject).
When I use the "Add" method, I get the error that: The object cannot be added to the ObjectStateManager because it already has an EntityKey. Use ObjectContext.Attach to attach an object that has an existing key.
When I use the "Attach" method, I get the error that: The object cannot be added to the ObjectStateManager because it already has an EntityKey. Use ObjectContext.Attach to attach an object that has an existing key.
I am getting quite frustrated, and I think I can hear the Entity Framework laughing at me.
Does anyone know how I can resolve this?
MyRole x = context.Roles.FirstOrDefault(a => a.RoleId == this.RoleId);
context.ApplyPropertyChanges("Roles", this);
foreach (MyPermission p in this.Permissions)
{
x.Permissions.Add(p);
// ^ or v
x.Permissions.Attach(p);
}
context.SaveChanges();
Thanks.
Wow. After 20 or so straight hours on this problem, I'm starting to hate the Entity Framework. Here is the code that appears to be working currently. I would appreciate any advice on how to make this more streamlined.
I did rework the WCF service so that there is only the one data context. Thanks Craig.
Then I had to change the code to the following:
MyRole x = context.Roles.FirstOrDefault(a => a.RoleId == this.RoleId);
if (x == null) // inserting
{
MyApplication t = this.Application;
this.Application = null;
context.Attach(t);
this.Application = t;
}
else // updating
{
context.ApplyPropertyChanges("Roles", this);
x.Permissions.Load();
IEnumerable<Guid> oldPerms = x.Permissions.Select(y => y.PermissionId);
List<MyPermission> newPerms = this.Permissions.Where(y => !oldPerms.Contains(y.PermissionId)).ToList();
IEnumerable<Guid> curPerms = this.Permissions.Select(y => y.PermissionId);
List<MyPermission> deletedPerms = x.Permissions.Where(y => !curPerms.Contains(y.PermissionId)).ToList();
// new
foreach (MyPermission p in newPerms)
{
x.Permissions.Add(context.Permissions.First(z => z.PermissionId == p.PermissionId));
}
// deleted
foreach (MyPermission p in deletedPerms)
{
x.Permissions.Remove(context.Permissions.First(z => z.PermissionId == p.PermissionId));
}
}
You are using multiple ObjectContexts concurrently (the variable context and whereever this came from). Don't do that. It will only make things very difficult for you. Use one ObjectContext at a time.
I can give more specific advice if you show more code.
I suspect you are getting the errors because the ObjectContext thinks you are trying to add a new entity but finds it already has a EntityKey. I use the AttachTo method of the ObjectContext to attach my already existing entities to their EntitySet. I have had results generating my entities from stubs or hitting the database. This way when you add the entity to the navigation property on your entity, the ObjectContext finds the entity in it's EntitySet and knows it is an existing entity and not a new one. I don't know if this is clear. I could post some code if it would help. As Mr Stuntz said in his answer, posting more of your code would help.