#Cacheable in Spring does not understand dynamically assigned values - spring-aop

I need to dynamically assign values of cacheResolver for #Cacheable in runtime because cacheResolver has the same value for #Cacheable in every method. Hence, I use Spring AOP to dynamically assign the value but then Spring does not recognize the newly added value for cacheResolver.
Seems that AOP load #Cacheable value at the beginning.
Anyone knows how to make it work?
My AOP code:
#Aspect
#Component
#Order(1)
public class CacheableAspect {
#Pointcut("#annotation(org.springframework.cache.annotation.Cacheable)")
public void cacheablePointCut() {}
#Before("cacheablePointCut()")
public void addCacheableResolver(JoinPoint joinPoint) {
Annotation cacheableAnnotation = getCacheableAnnotation(joinPoint);
Object handler = Proxy.getInvocationHandler(cacheableAnnotation);
Field f;
try {
f = handler.getClass().getDeclaredField("memberValues");
} catch (NoSuchFieldException | SecurityException e) {
throw new IllegalStateException(e);
}
f.setAccessible(true);
Map<String, Object> memberValues;
try {
memberValues = (Map<String, Object>) f.get(handler);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
memberValues.put("cacheResolver", "cacheableResolver");
}
private Annotation getCacheableAnnotation(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
return method.getAnnotation(Cacheable.class);
}
}
My #Cacheable code in which i want cacheResolver is dynamically assigned a value:
#Cacheable(value = "test")
public int test() {
System.out.println("xxx");
return 10;
}

OK, so you are trying to dynamically change an annotation representation in the JVM during runtime. Not only is that ugly, but it probably does not work as you hope it would. It seems you found out that specific annotations are represented by a dynamic proxy instance during runtime, then you are successfully manipulating one of its field values. But annotations are meant to be immutable, aber depending on when e.g. Spring scans the annotations while wiring the application, your approach to modify the proxy fields later, while being a nice try, just comes too late.
How about a more canonical approach to use multiple cache managers and/or a resolver which dynamically does what you need to begin with? As much as I love AOP, it is not the answer to everyhing.
By the way, even though your aspect is kind of useless in this case, at least we can use it as an example of how to bind annotation values to advice methods parameters, i.e. you do not need to fetch the annotation from the method by reflection next time you write an aspect:
#Pointcut("#annotation(cacheable)")
public void cacheablePointCut(Cacheable cacheable) {}
#Before("cacheablePointCut(cacheable)")
public void addCacheableResolver(JoinPoint joinPoint, Cacheable cacheable) {
Object handler = Proxy.getInvocationHandler(cacheable);
// (...)
}

Related

Generating complex types with all properties equivalent

Does AutoFixture have a facility to create multiple instances of a given type with all the same data? My classes are not serializable and I need two models which are not reference equivalent, but instead have matching properties.
public class Foo
{
// Many more properties and similar models needing the same semantics.
public string Name { get; set; }
}
var a = fixture.Create<Foo>();
var b = fixture.Create<Foo>();
Assert.False(ReferenceEquals(a, b));
Assert.Equal(a.Name, b.Name);
I don't think that AutoFixture can do this, but Albedo can. While this is only proof-of-concept code, it should illustrate the general idea, I hope.
Create a new class, deriving from ReflectionVisitor<T>:
public class PropertyCopyVisitor<T> : ReflectionVisitor<T>
{
private readonly T source;
private readonly T destination;
public PropertyCopyVisitor(T source, T destination)
{
this.source = source;
this.destination = destination;
}
public override IReflectionVisitor<T> Visit(
PropertyInfoElement propertyInfoElement)
{
var pi = propertyInfoElement.PropertyInfo;
pi.SetValue(this.destination, pi.GetValue(this.source));
return this;
}
public override T Value
{
get { return this.destination; }
}
}
The pivotal part of the implementation is the Visit overload where it uses Reflection to copy each property from source to destination object.
Since this implementation mutates destination, the Value property is never used, but it has to be there because it's abstract in ReflectionVisitor<T>...
You can now write your test as:
var fixture = new Fixture();
var a = fixture.Create<Foo>();
var b = fixture.Create<Foo>(); // Or just create a new, empty Foo...
// This copies all properties from a to b:
new TypeElement(typeof(Foo)).Accept(new PropertyCopyVisitor<Foo>(a, b));
Assert.False(ReferenceEquals(a, b));
Assert.Equal(a.Name, b.Name);
Here, I still used fixture to create b, but you don't have to do that, because all properties are going to be overwritten anyway. If Foo has a parameterless constructor, you could instead simply use new Foo(); it'd make no difference.
This proof-of-concept explicitly only copies properties. If you need to copy fields as well, you'll have to override the appropriate Visit method as well. Furthermore, if the objects in question take constructor arguments, you'll need to deal with those explicitly as well.
If you find it tedious to write new TypeElement(typeof(Foo)).Accept(new PropertyCopyVisitor<Foo>(a, b));, I'm sure you can figure out a way to write a helper method around it.
As an additional note, PropertyCopyVisitor doesn't have to be generic, because it doesn't actually use the T type argument for anything. I just like the type safety that this gives the constructor...

Spring data mongo ConditionalGenericConverter empty TypeDescriptor

I'm trying to implement a somewhat general converter, which transforms the data based on a given annotation. Say I want to transform these annotated strings in any matter.
All is well, until the code hits my converter's "matches" method. The "sourceType" I'm getting is always stripped out of all of the useful information. Has anyone had any success with such a setup, or am I missing something?
public class TestStringWriteConverter implements ConditionalGenericConverter {
#Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
if (sourceType.hasAnnotation(GivenAnnotation.class)) {
//->never gets executed, because sourceType is being stripped out of it's useful infos
}
I followed the problem to MappingMongoConverter from this package org.springframework.data.mongodb.core.convert
protected void writeInternal(Object obj, final DBObject dbo, MongoPersistentEntity<?> entity) {
//...
if (null != propertyObj) {
if (!conversions.isSimpleType(propertyObj.getClass())) {
writePropertyInternal(propertyObj, dbo, prop);
} else {
// I always end up here, which is correct but the whole prop object is being omitted in favor of the getFieldName() property
writeSimpleInternal(propertyObj, dbo, prop.getFieldName());
}
}
}
The spring versions I'm using:
<spring.version>3.2.5.RELEASE</spring.version>
<spring.data.version>1.3.2.RELEASE</spring.data.version>
Any help is much appreciated.
I think you misunderstand what sourceType.hasAnnotation(…) actually returns. As the name suggests, it inspects the type for annotations. So for a given type like this:
#MyAnnotation
class Foo { }
it would allow you to find #MyAnnotation. However you are writing about "annotated strings". I assume you mean something like:
class Bar {
#MyAnnotation
String property;
}
This is not a type annotation and the Converter API is not meant to cover such cases. If you think supporting such scenarios would be worthfile, please file a JIRA ticket.

Using stored procedures (Linq-to-SQL, not EF) in WCF RIA - Silverlight 4

For the love of heaven and earth I really wish someone could help me out with this issue. It seems everyone has something to say about EF but nothing about Linq-to-SQL.
I am trying to grab some data from my table via a stored procedure, believe me, that's all.
I added the Linq-to-SQL model (LAMP.dbml)
added the stored procedure (getAffectedParcel) from the server explorer. getAffectedParcel takes 2 strings as parameters
Build the application.
Added a domain service class (LAMPService)
Selected the (LAMPDataContext) as the data context class (normally I would tick generate metadata, but since I am not working with tables it's not enabled for ticking)
Added the following function to the LAMPService.cs:
public IEnumerable < getAffectedParcelResult > GetTheAffectedParcels(String v, String vf)
{
return this.DataContext.getAffectedParcel(v, vf).AsEnumerable();
}
Added the following code to a Silverlight page in an attempt to consume the stored procedure:
LAMPContext db = new LAMPContext();
try
{
var q = db.GetTheAffectedParcels("18606004005", "").Value;
foreach (getAffectedParcelResult GAP in q)
{
MessageBox.Show(GAP.Owner);
}
}
catch (Exception ex)
{
MessageBox.Show (ex.Message.ToString());
}
Build and run application. An error occurs stating:
Object reference not set to an instance of an object.
I have tried ~1000,000 ways to see if this thing would work, but to no avail. Please don't tell me to use Entity Framework, I want to use Linq-to-SQL. Can someone (anyone) help me out here.
//houdini
Calling a stored procedure from the Silverlight client happens in the Async world. Let's consider an example from the AdventureWorks database...
Here's what the Domain Service method looks like. It is calling the EF on a stored procedure in the database called 'BillOfMaterials'.
public IQueryable<BillOfMaterial> GetBillOfMaterials()
{
return this.ObjectContext.BillOfMaterials;
}
Back on the client side, here is the code for setting up the call...
public GetSp()
{
InitializeComponent();
DomainService1 ds1 = new DomainService1();
var lo = ds1.Load(ds1.GetBillOfMaterialsQuery());
lo.Completed += LoCompleted;
}
First, the Domain Service is created, and then it is used to load the results of the stored procedure. In this particular case, the result of this is an instance of 'LoadOperation'. These things are async, so the LoadOperation needs to have a callback for when it is finished. The callback code looks like this...
public ObservableCollection<BillOfMaterial> MyList { get; set; }
void LoCompleted(object sender, EventArgs e)
{
LoadOperation lo = sender as LoadOperation;
if(lo!=null)
{
MyList = new ObservableCollection<BillOfMaterial>();
foreach(BillOfMaterial bi in lo.AllEntities)
{
MyList.Add(bi);
}
dataGrid1.ItemsSource = MyList;
}
}
In this method, the 'sender' is dereferenced into the LoadOperation instance, and then all the goodies from the database can be accessed. In this trivial example, a list is built and passed to DataGrid as the ItemsSource. It's good for understanding, but you would probably do something else in practice.
That should solve your problem. :)
The best advice I can give on Silverlight and RIA is never do ANYTHING on your own until you have tried it in AdventureWorks. You will just waste your time and beat your head against the wall.
Firstly, it seems like your DomainService code is written for Invoke() rather than Query(). You should use Query as it enables you to update data back to the server.
Solution: you should add a [Query] attribute to GetTheAffectedParcels on the domain service.
[Query]
public IQueryable<Parcel>
GetTheAffectedParcels(string ParcelNumber, string LotNumber)
{
// etc.
}
Secondly, RIA Services needs to know which is the primary key on the Parcel class.
Solution: Apply a MetadataType attribute to the Parcel class, which allows you to add metadata to the Parcel class indirectly, since it is generated by Linq2Sql and you couldn't add annotations directly to the ParcelId - it'd get wiped away.
[MetadataType(typeof(ParcelMetadata)]
public partial class Parcel
{
}
public class ParcelMetadata
{
[System.ComponentModel.DataAnnotations.Key]
public int ParcelId {get; set; }
}
Thirdly, modify your client like this. Instead try this on the Silverlight client:
LAMPContext db = new LAMPContext();
try
{
var q = db.GetTheAffectedParcelsQuery("18606004005", "");
db.Load(q, (op) =>
{
if (op.HasError)
{
label1.Text = op.Error.Message;
op.MarkErrorAsHandled();
}
else
{
foreach (var parcel in op.Entities)
{
// your code here
}
}
}
}
catch (Exception ex)
{
label1.Text = op.ex.Message;
}
Much thanks to Chui and Garry who practically kicked me in the right direction :) [thanks guys...ouch]
This is the procedure I finally undertook:
-After adding the data model(LINQ2SQL) and the domain service, I created a partial class [as suggested by Chui] and included the following metadata info therein:
[MetadataTypeAttribute(typeof(getAffectedParcelResult.getAffectedParcelResultMetadata))]
public partial class getAffectedParcelResult
{
internal sealed class getAffectedParcelResultMetadata
{
[Key]
public string PENumber { get; set; }
}
}
Then, Adjusted the Domain Service to include the following:
[Query]
public IQueryable<getAffectedParcelResult> GetTheAffectedParcels(string v, string vf)
{
// IEnumerable<getAffectedParcelResult> ap = this.DataContext.getAffectedParcel(v, vf);
return this.DataContext.getAffectedParcel(v, vf).AsQueryable();
}
Then Build the app, afterwhich the getAffectedParcelResult store procedure appeared in the Data Sources panel. I wanted to access this via code however. Therefore, I accessed it in silverlight [.xaml page] via the following:
LAMPContext db = new LAMPContext();
var q = db.GetTheAffectedParcelsQuery("18606004005", "");
db.Load(q, (op) =>
{
if (op.HasError)
{
MessageBox.Show(op.Error.Message);
op.MarkErrorAsHandled();
}
else
{
foreach (getAffectedParcelResult gap in op.Entities)
{
ownerTextBlock.Text = gap.Owner.ToString();
}
}
},false);
This worked nicely. The thing is, my stored procedure returns a complex type so to speak. As of such, it was not possible to map it to any particular entity.
Oh and by the way this article helped out as well:
http://onmick.com/Home/tabid/154/articleType/ArticleView/articleId/2/Pulling-Data-from-Stored-Procedures-in-WCF-RIA-Services-for-Silverlight.aspx

C# setting form.visible = false inside a method?

hi i have this lines of code that i cant make it work
the goal is simple setting the form1 to visible = false
public static void DoActions(string Cmd){
if(Cmd == true)
{
MainForm.Visible = false;
}
}
but i keep on having this error
An object reference is required for
the non-static field, method, or
property
usually i set the called methond to static.. so the error will go away
but on this case how do i do it?
thanks for any help guys
'System.Windows.Forms.Control.Invoke(System.Delegate)'
This is happening because DoActions is a static method rather than an instance method, however MainForm is an instance field / property. The distinction is that instance methods operate on an instance of the class on which they are defined, wheras static methods do not.
This means that wheras instance methods are able to access properties, fields and methods of their containing class through the this keyword, for example:
// Instance field
Form1 MainForm;
void InstanceMethod()
{
Form1 frm = this.MainForm;
}
You cannot do the same thing from inside a static method (think about it, what instance would it operate on?). Note that C# will implicitly assume the use of the this keyword in places where it makes sense (so the above example could have been written as Form1 frm = MainForm).
See C# Static Methods for an alternative explanation of static vs instance methods (this is an important concept in object oriented programming that you should take the time to understand properly).
In your example you probably want to change DoActions to an instance method (by removing the static declaration):
public void DoActions(string Cmd)
{
if(Cmd == true)
{
this.MainForm.Visible = false;
}
}
This will allow it to access the instance MainForm field / property, however this may cause problems elsewhere in your code in places where you attempt to call DoActions from another static method without supplying an object instance.
Set form opacity and showintaskbar property in property window:
this.Opacity = 0;
this.ShowInTaskbar = false;
Your method is static - and so cannot access MainForm.
Make your method non-static if it is not required to be so.
public void DoActions(string Cmd)
{
if(Cmd == true)
{
MainForm.Visible = false;
}
}
If it is required, then create a static field in your class and ensure it is set before this method runs.

How to achieve "Blendability" when using DataServiceCollection in my ViewModel

I'm looking at using oData endpoints in my Silverlight client. Naturally, I'm doing MVVM and I want the project to be nice and "Blendable" (i.e. I must be able to cleanly use static data instead of the oData endpoints when in design mode.)
Now to the problem. I'd like to use the DataServiceCollection in my ViewModels, since it allows for nice bindable collections without having to worry too much with BeginExecute/EndExecute etc.
Now, let's look at some code. My Model interface looks like this:
public interface ITasksModel
{
IQueryable<Task> Tasks { get; }
}
The oData endpoint implementation of that interface:
public class TasksModel : ITasksModel
{
Uri svcUri = new Uri("http://localhost:2404/Services/TasksDataService.svc");
TaskModelContainer _container;
public TasksModel()
{
_container = new TaskModelContainer(svcUri);
}
public IQueryable<Task> Tasks
{
get
{
return _container.TaskSet;
}
}
}
And the "Blendable" design-time implementation:
public class DesignModeTasksModel : ITasksModel
{
private List<Task> _taskCollection = new List<Task>();
public DesignModeTasksModel()
{
_taskCollection.Add(new Task() { Id = 1, Title = "Task 1" });
_taskCollection.Add(new Task() { Id = 2, Title = "Task 2" });
_taskCollection.Add(new Task() { Id = 3, Title = "Task 3" });
}
public IQueryable<Task> Tasks
{
get {
return _taskCollection.AsQueryable();
}
}
}
However, when I try to use this last one in my ViewModel constructor:
public TaskListViewModel(ITasksModel tasksModel)
{
_tasksModel = tasksModel;
_tasks = new DataServiceCollection<Task>();
_tasks.LoadAsync(_tasksModel.Tasks);
}
I get an exception:
Only a typed DataServiceQuery object can be supplied when calling the LoadAsync method on DataServiceCollection.
First of all, if this is the case, why not make the input parameter of LoadAsync be typed as DataServiceQuery?
Second, what is the "proper" way of doing what I'm trying to accomplish?
The reason LoadAsync requires DataServiceQuery is that just plain IQueryable doesn't define asynchronous way of executing the query. The reason the method takes IQueryable type as its parameter is so that users don't have to cast the query object to DataServiceQuery explicitely (makes the code shorter) and since we assume that users will try to run their code at least once, they would see the error immediately (as you did).
LoadAsync only supports asynchronous operations, so it needs the DataServiceQuery. If you already have the results (without a need to execute async request) you can call the Load method instead. Which is the answer to your second question. Instead of calling LoadAsync for both desing time and run time, you could use Load for design time and LoadAsync for run time. But due to tracking constrains you might need to create the DataServiceCollection in different way.
Something like this:
DataServiceCollection<Task> dsc;
DataServiceQuery<Task> dsq = _tasksModel as DataServiceQuery<Task>;
if (dsq != null)
{
dsc = new DataServiceCollection<Task>();
dsc.LoadAsync(dsq);
}
else
{
dsc = new DataServiceCollection<Task>(myDataServiceContext);
dsc.Load(_tasksModel);
// Invoke the LoadAsyncCompleted handler here
}
If you pass the DataServiceContext to the constructor before caling Load the entities will be tracked (just like in the LoadAsync case). If you don't need that you can call the constructor which takes IEnumerable and TrackingMode and turn off tracking on it.

Resources