For testing purposes, I'd like to see all of the properties and their corresponding values of an HttpApplication object (I'm testing some functionality of an HTTPModule). My first thought was to serialize it to XML, then view that or write it to a file.
The problem is, HttpApplication is not a serializable class, so an exception is thrown when I try to serialize. Are there any other techniques, or is it even possible to get a string representation of a non-serializable object? I'd just like to see all of the same properties I get with Intellisense and their values.
I've seen some articles which mention Reflection, but I haven't found anything that suggests it would work for my scenario.
UPDATE:
After getting a couple responses, it looks like I'll need to use Reflection. Here is the code I'm using:
Dim sProps As New StringBuilder
For Each p As System.Reflection.PropertyInfo In oHttpApp.GetType().GetProperties()
If p.CanRead Then
sProps.AppendLine(p.Name & ": " & p.GetValue(oHttpApp, Nothing))
End If
Next
On my AppendLine statement, an exception is thrown right away:
System.InvalidCastException: Operator '&' is not defined for string
"Context: " and type 'HttpContext'. at
Microsoft.VisualBasic.CompilerServices.Operators.InvokeObjectUserDefinedOperator(UserDefinedOperator
Op, Object[] Arguments) at
Microsoft.VisualBasic.CompilerServices.Operators.InvokeUserDefinedOperator(UserDefinedOperator
Op, Object[] Arguments) at
Microsoft.VisualBasic.CompilerServices.Operators.ConcatenateObject(Object
Left, Object Right)
#granadaCoder, you mentioned that I'll need to know how "deep" to go, I'm wondering if this is the problem. In the error above, Context is an complex object so I would need to drill into that and get its individual properties, correct? Do you know how I might be able to do that - or would it be as simple as calling GetProperties again on p inside my loop?
Sounds like a good use case for reflection--
How to iterate through each property of a custom vb.net object?
You could iterate over all the object's properties and create your own XML/JSON view of them.
Update--
Here is c# code of how i turn any object to a dictionary (which would work for your use case)
public static Dictionary<string,string> ToDictionary<T>(this T me, string prefix=null) where T:class
{
Dictionary<string, string> res = new Dictionary<string, string>();
if (me == null) return res;
var bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.GetField;
var properties = me.GetType().GetProperties(bindingFlags)
.Where(i => i.CanRead
);
foreach (var i in properties)
{
var val = i.GetValue(me, null);
var str = "";
if (val != null)
str = val.ToString();
res[string.Format("{0}{1}", prefix, i.Name)] = str;
}
return res;
}
Some objects are not meant to be serializable. Take an IDataReader for an example.
You're gonna have to go with reflection. And "pluck off" the properties that are readable.
Here's some get-started code.
private void ReadSomeProperties( SomeNonSerializableObject myObject )
{
foreach( PropertyInfo pi in myObject.GetType( ).GetProperties( BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty) )
{
//pi.Name
//pi.GetValue( myObject, null )
//don't forget , some properties may only have "setters", look at PropertyInfo.CanRead
}
}
Of course, when the property is a complex object (not a scalar), then you have to figure out how "deep" you want to go digging.
Related
We are using Couchbase for our Session and for OutputCache.
In this context, how can we cache by a complex object that is being passed to the method using a Custom Model Binder that retrieves a value from the Session?
This is the signature of the method I want to cache with the OutputCache attribute:
[HttpGet]
[OutputCache(CacheProfile = "MyObjectsCache", VaryByParam = "myParam")]
public ActionResult Index([ModelBinder(typeof (CustomVariableSessionModelBinder<MyClass>))] MyClass myParam)
{
Note: The ModelBinder is being used here for reasons beyond me and I cannot change it.
MyClass is a complex object that has an Id. I want to use the Id as the caching identifier.
public class MyClass
{
public int Id{get;set;}
//Other Properties
This is how the object is being retrieved from Session:
var sessionKey = typeof (MyNamespace.MyClass).FullName;
var httpContext = HttpContext.Current;
MyNamespace.MyClass newObject = null;
if (httpContext.Session != null)
{
newObject = httpContext.Session[sessionKey] as MyNamespace.MyClass;
}
Is it possible yo use VaryByParam for this scenario or will I have to use VaryByCustom?
I haven't tested this, but it should work. It's pretty much your only option anyways, though.
In addition to the built in ways to vary, you can vary by "Custom". This will call into a method in Global.asax you'll need to override: GetVaryByCustomString. Importantly for you situation here, this method is passed HttpContext, so you should be able to look into the session. Essentially, the solution will look something like:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
var args = custom.ToLower().Split(';');
var sb = new StringBuilder();
foreach (var arg in args)
{
switch (arg)
{
case "session":
var obj = // get your object from session
// now create some unique string to append
sb.AppendFormat("Session{0}", obj.Id);
}
}
return sb.ToString();
}
This is designed to handle multiple different types of "custom" vary types. For example, if you wanted to vary by "User", which is common, you can merely add a case for that in your switch. The important part is that the string returned by this method is actually what the output cache varies on, so you want that to be unique for the situation. This is why I prefixed the object's id with "Session" here. For example, if you just added the id, let's say 123, and then in another scenario you varied by user and that string was composed of just the user's id, which happened to be 123 as well. It would be the same string to the output cache, and you'd end with some weird results. Just be mindful of what the custom string looks like.
Now, you'd just alter your OutputCache attribute like:
[OutputCache(CacheProfile = "MyObjectsCache", VaryByParam = "myParam", VaryByCustom = "Session")]
Note: to vary by multiple custom things at once, you'd separate them with a ; (based on how the code above works). For example: VaryByCustom = "Session;User"
My JsonFX serialization code works, but the object that I'm serializing contains a list of polymorphic entities, and they're all deserialized as their base type and not their actual type.
Here's my serialization code:
public static string Serialize(System.Object obj)
{
StringBuilder builder = new StringBuilder();
using (TextWriter textWriter = new StringWriter(builder))
{
JsonWriter writer = new JsonWriter(textWriter);
writer.Write(obj);
return builder.ToString();
}
}
public static T Deserialize<T>(string json)
{
using (TextReader textReader = new StringReader(json))
{
var jsonReader = new JsonReader(textReader);
return jsonReader.Deserialize<T>();
}
}
As you can see it's pretty straightforward. I'm also not decorating my classes with any attributes or anything special to make them serializable. Besides the polymorphic problem, it all just seems to be working properly.
So how can I get my polymorphic types to deserialize properly?.
Thanks.
You need to turn on type hinting. Here's example code (this is relevant to JsonFx v1.4, may or may not work with your version):
StringBuilder result = new StringBuilder(string.Empty);
JsonWriterSettings settings = JsonDataWriter.CreateSettings(true);
settings.TypeHintName = "__type";
JsonWriter writer = new JsonWriter(result, settings);
writer.Write(obj);
return result.ToString();
This will add extra data to your JSON string which looks something like:
"__type": "MyNamespace.MyClass, MyAssembly",
Which means it finds out what derived type it is based on the class name. If you change your class name or namespace name, it won't work anymore. Worse, you can't deserialize from your old JSON text data anymore, unless you mass replace all occurrences of the old class name and replace it with the new.
So you have to be careful with it.
EDIT: Forgot to mention that you have to edit the source code of JsonFx for this to work.
In JsonReader.cs, find the ReadArray method:
Change:
object value = this.Read(arrayItemType, isArrayTypeAHint);
to:
object value = this.Read(null, false);
This will ensure that JsonFx will always attempt to figure out the type of each element in an array/list. If you want this to work for just single variables, well you'd need to do the changes on the appropriate code (haven't tried that).
I'm trying to write a Compare method to compare properties in some POCOs using Reflection to ensure that they've been persisted to the database correctly. For example, let's say I have this POCO:
public class NoahsArk
{
public string Owner { get; set; }
public ICollection<Animal> Animals { get; set; }
}
What I want to do is this:
[Test]
public class Saves_Correctly_To_Database()
{
var noahsArk = new NoahsArk { // some setup code here };
db.Save(noahsArk);
var dbNoahsArk = db.Get<NoahsArk>(noahsArk.Id);
Assert.That(Compare(noahsArk, dbNoahsArk), Is.True);
}
The ORM I'm using is NHibernate. My Compare method looks like this so far:
public static bool EqualsProperties<T>(this T x, T y)
{
var xType = x.GetType();
foreach (var property in xType.GetProperties())
{
if (property.GetValue(x, null).Implements(typeof(ICollection<>)))
{
var xValue = property.GetValue(x, null) as ICollection<T>;
var yValue = property.GetValue(y, null) as ICollection<T>;
}
Object.Implements() is an extension method I wrote to determine if a type implements an interface. As you can see, the method is incomplete. The problem I'm running into is that when I use property.GetValue(x, null), it returns an object, and I don't know how to cast it into its specific generic ICollection type. I need to be able to do this so I can use LINQ to do a x.Contains(y) to compare the two collections for equality. Any idea on how to do this?
P.S. I tried using Compare .NET Objects, but it's giving me a null reference exception somewhere deep within NHibernate. It doesn't properly handle how NHibernate proxies the ICollection for lazy loading. To make matters worse, NHibernate modifies the POCO to support lazy-loading, but this is all done at runtime. In the source code, it looks like you're just working with a regular ICollection, but NHibernate changes this to NHibernate.Collections.Generic.PersistentSet at runtime, and this is what's causing the comparer to fail.
Your question is a bit confusing because you don't need the type parameter T in the declaration of your EqualsProperties method. You just need
public static bool EqualsProperties(this object x, object y)
You then go on to use the same parameter T to cast properties of x and y to ICollection<T>; however, the objects in these collections obviously may have a different type than x and y.
Now to answer your question: you don't need to cast to the correct generic type to use the LINQ Contains method. You can do something like this:
xValue = property.GetValue(x, null);
yValue = property.GetValue(y, null);
if (typeof(IEnumerable).IsInstanceOf(x))
{
IEnumerable<object> xEnumerable = (x as IEnumerable).Cast<object>();
IEnumerable<object> yEnumerable = (y as IEnumerable).Cast<object>();
// use any LINQ method you like now
}
You should also make sure you use the LINQ overloads that take an equality comparer, as your domain objects obviously do not override the Equals method themselves. Otherwise you wouldn't be writing this unit testing code to compare them.
Sharp architecture framework use attribute to decor properties which should be taken into the equals method. See the source code of DomainSignatureAttribute class and EntityWithTypedId<>.Equals method.
Background
Similar to this question, I need to determine if an entity in my NHibernate application is dirty or not. There is a "IsDirty" method on ISession, but I want to check a specific entity, not the whole session.
This post on nhibernate.info describes a method of checking an entity by fetching its database state and comparing it to the current state of the entity.
Problem
I've copied that method, but it isn't working for me. See the code:
public static Boolean IsDirtyEntity(this ISession session, Object entity)
{
String className = NHibernateProxyHelper.GuessClass(entity).FullName;
ISessionImplementor sessionImpl = session.GetSessionImplementation();
IPersistenceContext persistenceContext = sessionImpl.PersistenceContext;
IEntityPersister persister = sessionImpl.Factory.GetEntityPersister(className);
EntityEntry oldEntry = sessionImpl.PersistenceContext.GetEntry(entity);
if ((oldEntry == null) && (entity is INHibernateProxy))
{
INHibernateProxy proxy = entity as INHibernateProxy;
Object obj = sessionImpl.PersistenceContext.Unproxy(proxy);
oldEntry = sessionImpl.PersistenceContext.GetEntry(obj);
}
Object [] oldState = oldEntry.LoadedState;
Object [] currentState = persister.GetPropertyValues(entity, sessionImpl.EntityMode);
Int32 [] dirtyProps = persister.FindDirty(currentState, oldState, entity, sessionImpl);
return (dirtyProps != null);
}
The line that that populates the currentState array by calling persister.GetPropertyValues() is the problem. The array is full of nulls, instead of the actual values from my entity.
When I stepped into the code, I found that reflection is being used to get the values from the fields -- but the fields are null. This seems like a result of the proxy, but I'm not quite sure where to go from here.
I changed my default-access strategy from "field.camelcase-underscore" to "property" and now the persister.GetPropertyValues() method returns correct values.
Too early to declare victory, but seems interesting. I was using the field access strategy because I had code in my entities' properties to track dirty state. Since I'm removing that code and going to rely on NH to determine dirty state, I was able to use the property access strategy.
As I develop code, I often want to unit test some of the building blocks of a class even if they are normally private. If my unit tests are inside the project, I can use "Friend" to accomplish this and still keep the functions private for normal use. But I would rather move my NUnit tests into their own separate project(s). How do I achieve the effect I'm looking for?
You can't (easily) test private methods from a different project, but it's quite common to test internal methods (Friend in VB) from a test project using InternalsVisibleToAttribute. This makes Friend members accessible to another assembly.
Apparently this was new in VB 9 although it was available in C# 2... not quite sure why, but this blog post from Bart de Smet gives a quick example.
Note that if your production assembly is signed, your test assembly will need to be signed too, and you'll have to specify the public key in the InternalsVisibleToAttribute arguments. See this Stack Overflow answer for more details.
You can use Reflection to invoke the private methods. There are plenty of samples out there to do this.
From a quick google search: http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx
The basics: (this is pasted from the code project site linked above)
public static object RunStaticMethod(System.Type t, string strMethod,
object [] objParams)
{
BindingFlags eFlags =
BindingFlags.Static | BindingFlags.Public |
BindingFlags.NonPublic;
return RunMethod(t, strMethod,
null, aobjParams, eFlags);
} //end of method
public static object RunInstanceMethod(System.Type t, string strMethod,
object objInstance, object [] aobjParams)
{
BindingFlags eFlags = BindingFlags.Instance | BindingFlags.Public |
BindingFlags.NonPublic;
return RunMethod(t, strMethod,
objInstance, aobjParams, eFlags);
} //end of method
private static object RunMethod(System.Type t, string
strMethod, object objInstance, object [] aobjParams, BindingFlags eFlags)
{
MethodInfo m;
try
{
m = t.GetMethod(strMethod, eFlags);
if (m == null)
{
throw new ArgumentException("There is no method '" +
strMethod + "' for type '" + t.ToString() + "'.");
}
object objRet = m.Invoke(objInstance, aobjParams);
return objRet;
}
catch
{
throw;
}
} //end of method