Take for example the below sample code:
public class TestMultipleAttributesAttribute : AttributeWithPriority
{
public string HelpMessage { get; set; }
}
public class Student
{
[TestMultipleAttributes(HelpMessage = "Student")]
public virtual string Name { get; set; }
}
public class SpecificStudent : Student
{
[TestMultipleAttributes(Priority = 100, HelpMessage = "SpecificStudent")]
public override string Name { get; set; }
}
Is there any way by reflection how I can get both the two instances of the TestMultipleAttributes for the same overriden property?
I tried the below code:
[Test]
public void testMultipleAttribs()
{
var property = typeof(SpecificStudent).GetProperties().FirstOrDefault(x => x.Name == "Name");
var attribList = property.Attributes; //returns none
var customAttribs = property.CustomAttributes.ToList(); //returns 1
var customAttribs2 = property.GetCustomAttributes(inherit: true);// returns 1
int k = 5;
}
One solution that came in mind is to find the property.DeclaringType, and repeating the process , and get the attributes for it. However, I don't find it an elegant way to do this and was wondering if there is a better way.
You have only one attribute named TestMultipleAttributes and you overwrote it.The attribute have more than one property (Priority and HelpMessage) its working correctly.
You can create more "really" attributes like so StudentAttribute and SpecificStudentAttribute.
I don't know...this is relatively elegant (though I'm sure I'm missing at least one special case). It should enumerate all the custom attributes on an overridden or virtual property in the inheritance chain in nearest-first order:
public static IEnumerable<Attribute> AllAttributes( PropertyInfo pi )
{
if ( pi != null )
{
// enumerate all the attributes on this property
foreach ( object o in pi.GetCustomAttributes( false ) )
{
yield return (Attribute) o ;
}
PropertyInfo parentProperty = FindNearestAncestorProperty(pi) ;
foreach( Attribute attr in AllAttributesRecursive(parentProperty) )
{
yield return attr ;
}
}
}
private static PropertyInfo FindNearestAncestorProperty( PropertyInfo property )
{
if ( property == null ) throw new ArgumentNullException("property") ;
if ( property.DeclaringType == null ) throw new InvalidOperationException("all properties must belong to a type");
// get the property's nearest "ancestor" property
const BindingFlags flags = BindingFlags.DeclaredOnly
| BindingFlags.Public | BindingFlags.NonPublic
| BindingFlags.Static | BindingFlags.Instance
;
Type t = property.DeclaringType.BaseType ;
PropertyInfo ancestor = null ;
while ( t != null && ancestor == null )
{
ancestor = t.GetProperty(property.Name,flags) ;
t = t.BaseType ;
} ;
return ancestor ;
}
Related
I'd like to be able to update a key/value pair within a Document in a Collection without regard to the _id. This SO response appears to provide a way to do what I want, but it requires a focus on the _id key.
Essentially, what can I do to get my code, as shown below, to work without adding the [BsonIgnoreExtraElements] attribute to the class?
Based on the code which I'll show below, I think I'm close, and I do have a possible workaround, using the attribute, which I'll also show. However, I'd like to do this without decorating the classes with any attributes if possible.
Why no _id? Personally, I find the _id field just gets in the way. It's not part of any of my classes, and MongoDB isn't relational, so I simply don't use it. I freely admit that I may be completely missing the intent and idea behind the use of the _id field, but I simply don't see a need for it in MongoDB. Nevertheless, if possible, I'd like to focus on working without a focus on the _id field.
With that said, I have methods for retrieving a single document or an entire collection along with inserting a single document or entire collection, generically, without regard to the _id field.
I found that the key to ignoring the _id in a "select" is to use a Projection. I'm not all that familiar with the driver, but here are the lines of code that do the magic:
public const string ELEMENT_ID = "_id";
ProjectionDefinition<T> projection = Builders<T>.Projection.Exclude(ELEMENT_ID);
IFindFluent<T, T> found = mongoCollection.Find(bsonDocument).Project<T>(projection);
For background info, here is a retrieval method, ignoring _id:
public T GetSingle<T>(string property, string value) where T : class, new()
{
T tObject = null;
try
{
if (MongoContext.MongoClient != null && MongoContext.MongoDatabase != null)
{
string className = typeof(T).ToString();
int lastPeriod = className.LastIndexOf('.');
int length = className.Length - lastPeriod;
className = className.Substring(lastPeriod + 1, length - 1);
if (!string.IsNullOrEmpty(className))
{
IMongoCollection<T> mongoCollection = MongoContext.MongoDatabase.GetCollection<T>(className);
if (mongoCollection != null)
{
BsonDocument bsonDocument = new BsonDocument();
ProjectionDefinition<T> projection = Builders<T>.Projection.Exclude(ELEMENT_ID);
PropertyInfo[] propertyInfo = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
if (propertyInfo != null && propertyInfo.Length > 0)
{
IEnumerable<PropertyInfo> piExisting = propertyInfo.Where(pi => pi.Name.Equals(property, StringComparison.CurrentCultureIgnoreCase));
if (piExisting != null && piExisting.Any())
{
BsonValue bsonValue = BsonValue.Create(value);
BsonElement bsonElement = new BsonElement(property, bsonValue);
if (bsonElement != null)
{
bsonDocument.Add(bsonElement);
}
IFindFluent<T, T> found = mongoCollection.Find(bsonDocument).Project<T>(projection);
if (found != null)
{
tObject = found.FirstOrDefault<T>();
}
}
}
}
}
}
}
catch (Exception ex)
{
Logger.WriteToLog(Logger.LoggerMessage(ex));
}
return tObject;
}
Similar to other another SO response, I've tried to use FindOneAndUpdate, but I receive the following error:
Element '_id' does not match any field or property of class
ClassThingy.Suffix.
If I could apply the Projection to the FindOneAndUpdate somehow, I think that might resolve my issue, but I'm not able to find a way to do that application.
Here is my code:
public T UpdateSingle<T>(T item, string property, object originalValue, object newValue) where T : class, new()
{
string className = string.Empty;
T updatedDocument = null;
try
{
if (MongoContext.MongoClient != null && MongoContext.MongoDatabase != null)
{
className = ClassUtility.GetClassNameFromObject<T>(item);
if (!string.IsNullOrEmpty(className))
{
IMongoCollection<T> mongoCollection = MongoContext.MongoDatabase.GetCollection<T>(className);
if (mongoCollection != null)
{
BsonDocument bsonDocument = new BsonDocument();
ProjectionDefinition<T> projection = Builders<T>.Projection.Exclude(ELEMENT_ID);
PropertyInfo[] propertyInfo = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
if (propertyInfo != null && propertyInfo.Length > 0)
{
IEnumerable<PropertyInfo> piExisting = propertyInfo.Where(pi => pi.Name.Equals(property, StringComparison.CurrentCultureIgnoreCase));
if (piExisting != null && piExisting.Any())
{
BsonValue bsonValue = BsonValue.Create(originalValue);
BsonElement bsonElement = new BsonElement(property, bsonValue);
if (bsonElement != null)
{
bsonDocument.Add(bsonElement);
}
IFindFluent<T, T> found = mongoCollection.Find(bsonDocument).Project<T>(projection);
if (found != null)
{
FilterDefinition<T> filterDefinition = Builders<T>.Filter.Eq(property, originalValue);
UpdateDefinition<T> updateDefinition = Builders<T>.Update.Set(property, newValue);
updatedDocument = mongoCollection.FindOneAndUpdate<T>(filterDefinition, updateDefinition);
}
}
}
}
}
}
}
catch (Exception ex)
{
Logger.WriteToLog(Logger.LoggerMessage(ex));
}
return updatedDocument;
}
Interestingly enough, while the FindOneAndUpdate method actually appears to succeed and the "Suffix" collection does, in fact, get modified. Also, the return doesn't contain the modification. Instead, it's the "original" (when I use the workaround as shown below).
More Info:
Suffix Class:
public class Suffix
{
public string Code { get; set; }
public string Description { get; set; }
}
Suffix suffix = new Suffix();
MongoRepository.MongoRepository mongoRepository = new MongoRepository.MongoRepository("MyDataBase");
mongoRepository.UpdateSingle<Suffix>(suffix, "Description", "Jr", "Junior");
Workaround:
[BsonIgnoreExtraElements]
public class Suffix
{
public string Code { get; set; }
public string Description { get; set; }
}
But, much rather not use the attribute if at all possible.
One thing you're missing here is the third parameter of .FindOneAndUpdate() method which is of type FindOneAndUpdateOptions<T,T>. That's the place where you can define if you want to get document After or Before the modification. Before is the default value Moreover you can specify the projection and exclude _id property. Try:
FilterDefinition<T> filterDefinition = Builders<T>.Filter.Eq(property, originalValue);
UpdateDefinition<T> updateDefinition = Builders<T>.Update.Set(property, newValue);
ProjectionDefinition<T, T> projection = Builders<T>.Projection.Exclude("_id");
var options = new FindOneAndUpdateOptions<T>()
{
Projection = projection,
ReturnDocument = ReturnDocument.After
};
updatedDocument = mongoCollection.FindOneAndUpdate<T>(filterDefinition, updateDefinition, options);
I'm trying to find a fields in a class has a Obsolete attribute ,
What I have done is , but even thought the type has an obselete attribute its not found during iteration :
public bool Check(Type type)
{
FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var field in fields)
{
if (field.GetCustomAttribute(typeof(ObsoleteAttribute), false) != null)
{
return true
}
}
}
EDIT :
class MyWorkflow: : WorkflowActivity
{
[Obsolete("obselset")]
public string ConnectionString { get; set; }
}
and using it like this , Check(typeof(MyWorkflow))
Problem is that ConnectionString is nor Field and nor NonPublic.
You should correct BindingFlags and also, use GetProperties method to search for properties.
Try the following
public static bool Check(Type type)
{
var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
return props.Any(p => p.GetCustomAttribute(typeof(ObsoleteAttribute), false) != null);
}
Situation:
Currently I am reading all available functions and methods from specific class with reflection. All the functions in the class does have an attribute section like this
[ImportFunctionAttribute("myFunction1", 1, ImportAttribute.SourceType.Type1)]
public ImportStatusDetails myFunction1()
{
}
[ImportFunctionAttribute("myFunction2", 2, ImportAttribute.SourceType.Type2)]
public ImportStatusDetails myFunction2()
{
}
To get all the methods in the given class I use this code
// get the public methods out of import class order by class name
var importMethods = (typeof (Import)).GetMethods(
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
Problem:
Using the GetMethods-Method will give me both functions - but I only want to get the method of type ImportAttribute.SourceType.Type2.
Question:
Is it possible to limit the results of the GetMethods-Method for given CustomAttributes like for example with GetMethods(...).Where() ?? What should I do to resolve this problem?
Addition1
public class ImportFunctionAttribute : Attribute
{
public enum SourceType
{
Unknown = 0,
Type1 = 1,
Type2 = 2
}
public ImportFunctionAttribute(string psTabName, int pnTabId, SourceType pSourceType)
{
tabName = psTabName;
tabId = pnTabId;
source = pSourceType;
}
protected string tabName;
protected int tabId;
protected SourceType source;
public string TabName { get { return tabName; } }
public int TabId { get { return tabId; } }
public SourceType Source { get { return source; } }
}
Thanks in advance.
I believe you're looking for GetCustomAttributes(...).
var methods = new List<MethodInfo>();
var importMethods = (typeof (Import)).GetMethods(
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly));
for (var i = 0; i < importMethods.Length; i++)
{
var attr = importMethods[i].GetCustomAttributes(typeof(ImportFunctionAttribute), false).Cast<ImportFunctionAttribute>();
if (attr.Source == SourceType.Type2)
{
methods.Add(importMethods[i]);
}
}
I'm working with specflow and selenium, and I'm making a table to structure the user inputs in a website.
| Input1 | Input2 | Input3 |
| Opt1 | OptX | Opt2 |
And I built a class for the input:
public class ObjectDTO
{
public string Input1;
public Input2Type Input2;
public string Input3;
}
And an enum for one specific opt (since its a static input)
public enum Input2Type
{
[StringValue("OptX")]
X,
[StringValue("OptY")]
Y,
[StringValue("OptZ")]
Z
}
When I get the input I try to instantiate the object:
ObjectDTO stocks = table.CreateInstance<ObjectDTO>();
But it says 'No enum with value OptX found'.
Unfortunately the table.CreateInstance() is a fairly naive method and fails for Enums (among other things). I ended up writing an extension method for table that uses reflection to interrogate the object instance it is trying to create. The nice thing about this approach is that the method will only overwrite the instance values that are specified in the table columns, however, you have to call it for each row of the table and pass in an already instantiated instance. It could be easily modified to act just like the CreateInstance method, but for my purposes, this worked better...
public static class TableExtensions
{
public static void FillInstance(this Table table, TableRow tableRow, object instance) {
var propertyInfos = instance.GetType().GetProperties();
table.Header.Each(header => Assert.IsTrue(propertyInfos.Any(pi => pi.Name == header), "Expected to find the property [{0}] on the object of type [{1}]".FormatWith(header, instance.GetType().Name)));
var headerProperties = table.Header.Select(header => propertyInfos.Single(p => p.Name == header)).ToList();
foreach (var propertyInfo in headerProperties) {
object propertyValue = tableRow[propertyInfo.Name];
var propertyType = propertyInfo.PropertyType;
if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) {
propertyType = propertyType.GetGenericArguments().Single();
}
var parse = propertyType.GetMethod("Parse", new[] { typeof(string) });
if (parse != null) {
// ReSharper disable RedundantExplicitArrayCreation
try {
propertyValue = propertyType.Name.Equals("DateTime") ? GeneralTransformations.TranslateDateTimeFrom(propertyValue.ToString()) : parse.Invoke(null, new object[] { propertyValue });
} catch (Exception ex) {
var message = "{0}\r\nCould not parse value: {2}.{3}(\"{1}\")".FormatWith(ex.Message, propertyValue, parse.ReturnType.FullName, parse.Name);
throw new Exception(message, ex);
}
// ReSharper restore RedundantExplicitArrayCreation
}
propertyInfo.SetValue(instance, propertyValue, null);
}
}
}
And you could invoke it as follows:
ObjectDTO objectDTO;
foreach (var tableRow in table.Rows)
{
objectDTO = table.FillInstance(tableRow, new ObjectDTO());
//Do individual row processing/testing here...
}
I need to implement mechanism that compares two business objects and return the list of differences (past value, new value, isDifferenceBetter).
Because not all fields of class has to be compared and one fields need to be compared with different function then the other (sometimes < is better sometimes > is better ... ) I figured out that I need to implelemnt custom attribute and give it to each field that has to be compared in this object.
This attribute must have:
- name
- delegate or sth to point to the function which would be applied for comparision (dont know how to do it so far)
So could anyone suggest me if its a good idea? Maybe any other ideas.
Using attributes I would be able to use refflection to iterate through each field with attribute and invoke needed delegate.
thanks for help
bye
See my example below.
May be, it can help you:
namespace ConsoleApplication5
{
class FunctionToCompareAttribute : Attribute
{
public FunctionToCompareAttribute( String className, String methodName )
{
ClassName = className;
MethodName = methodName;
}
public String ClassName
{
get;
private set;
}
public String MethodName
{
get;
private set;
}
}
class ComparableAttribute : Attribute
{
}
class CompareResult
{
}
[Comparable]
class ClassToCompare
{
[FunctionToCompare( "ConsoleApplication5.ClassToCompare", "MyCompareFunction" )]
public String SomeProperty
{
get;
private set;
}
public static CompareResult MyCompareFunction( Object left, Object right, String propertyName )
{
return null;//Comparsion
}
}
class Program
{
static void Main( string[] args )
{
var left = new ClassToCompare();
var right = new ClassToCompare();
var type = typeof( ClassToCompare );
var typeAttributes = type.GetCustomAttributes( typeof( ComparableAttribute ), true );
if ( typeAttributes.Length == 0 )
return;
foreach ( var property in type.GetProperties() )
{
var attributes = property.GetCustomAttributes( typeof( FunctionToCompareAttribute ), true );
if ( attributes.Length == 0 )
continue;
var compareAttribute = attributes[ 0 ] as FunctionToCompareAttribute;
var className = compareAttribute.ClassName;
var methodName = compareAttribute.MethodName;
var compareType = Type.GetType( className );
var method = compareType.GetMethod( methodName, new Type[] { type, type, typeof( String ) } );
var **result** = method.Invoke( null, new Object[] { left, right, property.Name } ) as CompareResult;
}
}
}
}
Do some search about self tracking objects and the way ORMs(like NHibernate) checking an object for dirty fields
Certainly possible but perhaps you should be thinking along more abstract terms. Maybe a pair of attributes [LowerValueIsBetter] and [HigherValueIsBetter] would enable you to express this information in a more cohesive way.