I'm using ObjectDB but also want to make the collection inside a persisted object observable, so I have declared it this way:
#OneToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL) List<Widget> widgets = [] as ObservableList
Later on, I create a listener closure and attach it:
def widgetChangeListener = {
log.debug "WIDGET CHANGE: $it"
}
widgets.addPropertyChangeListener(widgetChangeListener)
However, when I try to persist the collection, I get this error:
Attempt to store an instance of a non persistable type com.greymatter.strategy.Harness$_closure1 - field com.greymatter.strategy.Harness.widgetChangeListener (error 303)
Is there any way to make this collection persistable while keeping the closure volatile, so I can observe changes to it? ObjectDB has a #Transient annotation, but I'm not sure how to apply it to the closure. If I put it on the def of widgetChangeListener, I get a MissingMethodException.
Are ObjectDB and ObservableList mutually exclusive?
Groovy collections are not fully supported by ObjectDB (and by JPA in general). See the list of supported collections in JPA / ObjectDB on this ObjectDB Manual page.
If ObservableList works well, except the listeners, you may use JPA lifecycle events to clear listeners before persisting or updating the entity object (and then set them back if necessary).
Alternatively you can keep 2 list fields in your entity class. An ordinary List that will be persisted, and an ObservableList wrapper of that list that will be set as transient.
Related
I need to log custom dimensions to Application Insights for which I'm using ILogger.BeginScope(). That works perfectly. That is:
using (logger.BeginScope(new Dictionary<string, object> { "key": "value" }))
{
logger.LogInformation("message");
}
My issue is that I need to call other methods in other classes, and I'm injecting the ILogger into all my classes. So how can I persist the logging scope among all my classes?
I could surely do ILogger.BeginScope() in all my classes, but I would need to pass the custom properties to classes that don't really need that information. Is there a pattern I could use?
If you call BeginScope multiple times in multiple classes within the same execution path, you will get one aggregated scope. There is no need to manually pass the properties. ILogger is injected as a singleton by default (see source code).
See also Since ILogger<T> is a singleton, how different threads can use BeginScope() without affecting others?
A call to BeginScope will put a new item onto that stack, and the adjoining Dispose will pop it off of that stack.
When the logger is invoked via LogInformation or otherwise, the data of the current stack object will be copied to write it to the console or whatever output that logger instance is configured to do.
LoggerExternalScopeProvider.cs#L14
There seems be some things missing in the Spring-LDAP ODM annotations. This is a question by way of a feature request, if there is a better way to contribute such requests, please say so.
I'd like to mark an #Attribute as read-only, so it will populate the bean from LDAP for reference, but not persist it back to ldap. I'd suggest adding an attribute read-only to #Attribute, defaulting to false, for the usual case. The default attributes of * misses all the operational attributes, some of which are very useful, and transfers more data than is required, slowing down the ldap query with attributes which will never be used.
An example of this; it would be very useful, for literally read only, such as entryUUID, etag, etc., which you cannot use if you wish to persist only some fields back to ldap, as the bean fails to persist to ldap with an exception when you save the bean. But also would be usefule for general fields which you want to structurally prevent the user from ever updating.
You can get around this by not annotating read-only fields, and then manually populating the read only fields with a separate call. Very messy and kills the query speed.
Also on a related topic, query() coudl have a default list of attributes, which you have already annotated in your classes, something like :
public static String[] getBeanAttributes(Class<?> beanClass) {
ArrayList<String> attrsObj = new ArrayList<>();
for (Field field : beanClass.getDeclaredFields()) {
if (field.isAnnotationPresent(Attribute.class)) {
Attribute attr = field.getAnnotation(Attribute.class);
attrsObj.add(attr.name());
}
}
String[] attrs = attrsObj.toArray(new String[attrsObj.size()]);
return attrs;
}
Above just returns a simple String[] of your declared attributes, to pass to query.attributes() - now i realize that as a static member, query() is built before the bean class is known, but at least there could be a helper function like the above, or a method signature for query attributes() that took a bean Class signature as an argument.
I created LDAP-312 on Jira. Thanks.
I have an NSManagedObject subclass with a transient property which is basically a reformatting of one of the persistent to-many relationships. I do this by observing the relationship with KVO and registering the observer in -awakeFromFetch, -awakeFromInsert, etc.
This all works fine, however if I pass the object between threads using the object's objectID and -objectWithID: technique there is no life-cycle method into which I can hook generation of the transient property. None of the life-cycle methods are triggered, in fact, since accessing the object directly using the id isn't considered a fetch, it seems.
There are ways around this, but it would be nice to use a life-cycle based technique. Am I perhaps missing something? Is there another standard method I could be using?
Thanks
Edit: Demonstration project
https://mega.co.nz/#!UsNBTZ7S!UU1qaFuc4W6Z2EYey-9AiMyfM8203Zfrm1lfpG5QITU
When you have a NSManagedObject instance on one thread with a context and then retrieve it from a different thread and different context the -awakeFromFetch or -awakeFromInsert fires.
Are you using the contexts properly so that you are retrieving a new instance?
Have you looked at the pointers in the debugger to make sure you are talking to a new instance of the NSManagedObject?
In my experience those lifecycle methods fire per context.
Ok, so to answer my own question, the issue is caused because objectWithID: always returns an object, even though the object isn't registered in the receiver managed object context. Seems in some circumstances objectRegisteredForID: is more informative. In any case, the conclusion is that the life-cycle methods do fire, but to take care with objectWithID: since it can result in an inconsistent object.
I encountered this same issue, and solved it by creating an extension on NSManagedObjectContext that goes through the normal fetch pathway, therefore triggering all of the expected lifecycle methods:
extension NSManagedObjectContext {
func fetchObject<T: NSManagedObject>(with objectID: NSManagedObjectID) -> T? {
let request = T.fetchRequest()
request.predicate = NSPredicate(format: "SELF = %#", objectID)
guard let result = try? fetch(request) else {
return nil
}
return result.first as? T
}
}
Is there a way we can use ObjectContext with DbContext's ModelBuilder? We don't want to use POCO because we have customized property code that does not modify entire object in update, but only update modified properties. Also we have lots of serialisation and auditing code that uses EntityObject.
Since poco does create a proxy with EntityObject, we want our classes to be derived from EntityObject. We don't want proxy. We also heavily use CreateSourceQuery. The only problem is EDMX file and its big connection string syntax web.config.
Is there any way I can get rid of EDMX file? It will be useful as we can dynamically compile new class based on reverse engineering database.
I would also like to use DbContext with EntityObject instead of poco.
Internal Logic
Access Modified Properties in Save Changes which is available in ObjectStateEntry and Save them onto Audit with Old and New Values
Most of times we need to only check for Any condition on Navigation Property for example
User.EmailAddresses.CreateSourceQuery()
.Any( x=> x.EmailAddress == givenAddress);
Access Property Attributes, such as XmlIgnore etc, we rely heavily on attributes defined on the properties.
A proxy for a POCO is a dynamically created class which derives from (inherits) a POCO. It adds functionality previously found in EntityObject, namely lazy loading and change tracking, as long as a POCO meets requirements. A POCO or its proxy does not contain an EntityObject as the question suggests, but rather a proxy contains functionality of EntityObject. You cannot (AFAIK) use ModelBuilder with EntityObject derivatives and you cannot get to an underlying EntityObject from a POCO or a proxy, since there isn't one as such.
I don't know what features of ObjectContext does your existing serialisation and auditing code use, but you can get to ObjectContext from a DbContext by casting a DbContext to a IObjectContextAdapter and accessing IObjectContextAdapter.ObjectContext property.
EDIT:
1. Access Modified Properties in Save Changes which is available in ObjectStateEntry and Save them onto Audit with Old and New Values
You can achieve this with POCOs by using DbContext.ChangeTracker. First you call DbContext.ChangeTracker.DetectChanges to detect the changes (if you use proxies this is not needed, but can't hurt) and then you use DbCotnext.Entries.Where(e => e.State != EntityState.Unchanged && e.State != EntityState.Detached) to get DbEntityEntry list of changed entities for auditing. Each DbEntityEntry has OriginalValues and CurrentValues and the actual Entity is in property Entity.
You also have access to ObjectStateEntry, see below.
2. Most of times we need to only check for Any condition on Navigation Property for example:
User.EmailAddresses.CreateSourceQuery().Any( x=> x.EmailAddress == givenAddress);
You can use CreateSourceQuery() with DbContext by utilizing IObjectContextAdapter as described previously. When you have ObjectContext you can get to the source query for a related end like this:
public static class DbContextUtils
{
public static ObjectQuery<TMember> CreateSourceQuery<TEntity, TMember>(this IObjectContextAdapter adapter, TEntity entity, Expression<Func<TEntity, ICollection<TMember>>> memberSelector) where TMember : class
{
var objectStateManager = adapter.ObjectContext.ObjectStateManager;
var objectStateEntry = objectStateManager.GetObjectStateEntry(entity);
var relationshipManager = objectStateManager.GetRelationshipManager(entity);
var entityType = (EntityType)objectStateEntry.EntitySet.ElementType;
var navigationProperty = entityType.NavigationProperties[(memberSelector.Body as MemberExpression).Member.Name];
var relatedEnd = relationshipManager.GetRelatedEnd(navigationProperty.RelationshipType.FullName, navigationProperty.ToEndMember.Name);
return ((EntityCollection<TMember>)relatedEnd).CreateSourceQuery();
}
}
This method uses no dynamic code and is strongly typed since it uses expressions. You use it like this:
myDbContext.CreateSourceQuery(invoice, i => i.details);
I have used many JSON object in applicationScope, sessionScope, and viewScope to track related data. Writing and reading these in SSJS is very simple:`
//Create a app scope variable
applicationScope.put("myvarname", {p1:"part 1", p2:"part2"});
// read and use the app scope variable ...
var myvar = applicationScope.get("myvarname");
//Work with parts as myvar.p1, myvar.p2, etc...
In the Java code I have been writing I have learned to read these variables which were written using SSJS using the com.ibm.jscript.std.ObjectObject package with code like this:
ObjectObject myvar = (ObjectObject) ExtLibUtil
.getApplicationScope().get(dbkey);
FBSValue localFBS = myvar.get("p1");
String myp1 = localFBS.stringValue();
localFBS = myvar.get("p2");
String myp2 = localFBS.stringValue();
Now, of course, I want to write a new entry using the Java Bean that can then be read by SSJS and other Java Beans in the same manner. I managed to write to the scope using a Map and a Hashtable, but these crash the logic when trying to read using the ObjectObject.
So, how would I go about building a new entry in the scope using the ObjectObject and/or FBSValue packages? I cannot find how to create a new FBSValue that can then be added to an ObjectObject. I am sure it is a simple thing a Newbs like me has missed.
/Newbs
You can construct an empty ObjectObject, populate it with FBSValues, and just put it directly into the scope Map:
ObjectObject myvar = new ObjectObject();
try {
myvar.put("p1", FBSUtility.wrap("part 1"));
myvar.put("p2", FBSUtility.wrap("part 2"));
} catch (InterpretException e) {
e.printStackTrace();
}
Map<String, Object> applicationScope = ExtLibUtil.getApplicationScope();
applicationScope.put("myvarname", myvar);
When retrieving it later (as in the examples you provided), SSJS will see it as JSON, Java will see it exactly as it was stored.
If you need to store deeper hierarchies, you can put instances of ArrayObject and ObjectObject inside an ObjectObject in addition to primitives, so, just like JSON itself, you can nest these as deep as you need.
Just be sure to only include true JSON (strings, numbers, booleans, arrays, objects) if you'll be storing it anywhere higher than the requestScope; specifically, FunctionObject does not implement Serializable, so JSON is safe to store, JavaScript is not. Strictly speaking, this only becomes toxic when stored in the viewScope in 8.5.2 and 8.5.3 (and even then, only when the application's persistence option is not set to keep all pages in memory). But if IBM ever implements cluster support, then all objects stored in sessionScope and applicationScope will need to be serializable to allow for inter-server state transport... so, in the interest of future-proofing the design, it's wise to hold to this principle for anything stored longer than the duration of a single request.