Combining Multiple Attributes to a Single Attribute - Merge Attributes

Combining multiple Attributes to a single Attribute - Merge Attributes

It depends to the framework which is using the attribute.

Combining attributes can be meaningful in order to the context which uses and interprets attributes. For example for those contexts which use .Net Type Description mechanisms you can customize the type description which .Net returns to consumers.

It's possible to provide custom metadata for types using the standard .Net mechanism for that purpose, registering a custom type descriptor for your object.

The idea will work this way, you create a custom type descriptor for your type. In the custom type descriptor, you return custom property descriptors for the properties of your type and in the property descriptor, you return a custom set of attributes for the property.

The approach requires more code, but it's really interesting and shares some good idea about how to provide custom metadata for your types:

IMetedataAttribute Interface

The usage is providing an standard way to create MetaDataAttributes. Each attribute which implements this interface will be used as metadata and instead of the attribute, those one which it returns in Process method will be used:

public interface IMetadatAttribute
{
Attribute[] Process();
}

Sample MetadataAttribute

It's a sample metadata attribute which returns some attribute instead when processing the attribute:

public class MySampleMetadataAttribute : Attribute, IMetadatAttribute
{
public Attribute[] Process()
{
var attributes = new Attribute[]{
new BrowsableAttribute(false),
new EditorBrowsableAttribute(EditorBrowsableState.Never),
new BindableAttribute(false),
new DesignerSerializationVisibilityAttribute(
DesignerSerializationVisibility.Hidden),
new ObsoleteAttribute("", true)
};
return attributes;
}
}

Property Descriptor

This class will be used by the custom type descriptor to provide a custom list of attributes for the property:

public class MyPropertyDescriptor : PropertyDescriptor
{
PropertyDescriptor original;
public MyPropertyDescriptor(PropertyDescriptor originalProperty)
: base(originalProperty) { original = originalProperty;}
public override AttributeCollection Attributes
{
get
{
var attributes = base.Attributes.Cast<Attribute>();
var result = new List<Attribute>();
foreach (var item in attributes)
{
if(item is IMetadatAttribute)
{
var attrs = ((IMetadatAttribute)item).Process();
if(attrs !=null )
{
foreach (var a in attrs)
result.Add(a);
}
}
else
result.Add(item);
}
return new AttributeCollection(result.ToArray());
}
}
// Implement other properties and methods simply using return original
// The implementation is trivial like this one:
// public override Type ComponentType
// {
// get { return original.ComponentType; }
// }
}

Type Descriptor

This is the type descriptor which provides a custom description for your type. In this example it uses custom property descriptors to provide custom attributes set for the properties of your class:

public class MyTypeDescriptor : CustomTypeDescriptor
{
ICustomTypeDescriptor original;
public MyTypeDescriptor(ICustomTypeDescriptor originalDescriptor)
: base(originalDescriptor)
{
original = originalDescriptor;
}
public override PropertyDescriptorCollection GetProperties()
{
return this.GetProperties(new Attribute[] { });
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
var properties = base.GetProperties(attributes).Cast<PropertyDescriptor>()
.Select(p => new MyPropertyDescriptor(p))
.ToArray();
return new PropertyDescriptorCollection(properties);
}
}

Typedescriptor Provider

This class will be used in the attribute above your type to introduce the custom type descriptor which we created as the metadata engine for the type:

public class MyTypeDescriptionProvider : TypeDescriptionProvider
{
public MyTypeDescriptionProvider()
: base(TypeDescriptor.GetProvider(typeof(object))) { }

public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType,
object instance)
{
ICustomTypeDescriptor baseDescriptor = base.GetTypeDescriptor(objectType, instance);
return new MyTypeDescriptor(baseDescriptor);
}
}

Sample Class

Here is my sample class which its Name property is decorated using MySampleMetadataAttribute and the class itself is registered to use our custom type descriptor provider:

[TypeDescriptionProvider(typeof(MyTypeDescriptionProvider))]
public class MySampleClass
{
public int Id { get; set; }
[MySampleMetadataAttribue]
[DisplayName("My Name")]
public string Name { get; set; }
}

To see the result it's enough to create an instance of the class and see the result in PropertyGrid:

var o = new MySampleClass();
this.propertyGrid1.SelectedObject = o;

Some notes on answer

  • Probably it's not as simple as you expected for such task. But it's working.
  • It's a lengthy answer, but contains a complete working example of how you can apply type descriptors to your types to provide custom metadata.
  • The approach will not work for engines which use reflection instead of type description. But it's completely working with for example PropertyGrid control which works with type description.

Combine the attributes from two different columns to create a single attribute

Does this work:

library(dplyr)
library(stringr)
df %>% mutate(name = str_c(lead(name),value, sep = '_'), value = lead(value)) %>%
filter(str_detect(name, '^b'))
# A tibble: 4 x 3
ID name value
<dbl> <chr> <chr>
1 1 b_dry 165
2 1 b_pine 677
3 1 b_moist 251
4 2 b_pine 804

How to create new attribute by combining existing 2 attribute in Weka?

Such a comparison is possible using the MathExpression filter as part of filter processing pipeline, using the ifelse construct. However, MathExpression does not allow you to use labels, but you can have an indicator label 0 or 1 to indicate whether gain or loss.

MultiFilter
|
+- Add (we insert a new numeric attribute, all missing values)
|
+- ReplaceMissingWithUserConstant (MathExpression skips missing values, hence replacing them in our new attribute)
|
+- MathExpression (the actual comparison between the two attributes)
|
+- NumericToNominal (to turn the numeric 0/1 values into labels)

I will demonstrate how to construct this pipeline using the bolts UCI dataset, which has the following attributes:

1 RUN     numeric
2 SPEED1 numeric
3 TOTAL numeric
4 SPEED2 numeric
5 NUMBER2 numeric
6 SENS numeric
7 TIME numeric
8 T20BOLT numeric

For this example, I want to compare SENS and TIME, creating an indicator whether SENS > TIME.

MultiFilter

The MultiFilter instance combines all our sub-filters into a single filter setup. That way you can easily apply, extend it or use it within a FilteredClassifier setup.

Add

First, we will add an attribute using the Add filter at index 8, which will push the class attribute to position 9, giving it the name SENS>TIME (you can give it any name you want):

weka.filters.unsupervised.attribute.Add -N SENS>TIME -C 8

ReplaceMissingWithUserConstant

Next, we use the ReplaceMissingValueUserConstant filter to replace the missing values in our attribute (index 8) with a dummy value, e.g., -1. This is unfortunately necessary, since MathExpression does not operate on missing values.

weka.filters.unsupervised.attribute.ReplaceMissingWithUserConstant -A 8 -R -1 -F "yyyy-MM-dd\'T\'HH:mm:ss"

MathExpression

With the stage set, we can now use MathExpression to fill in our comparison using the expression ifelse(A6>A7,1,0):

weka.filters.unsupervised.attribute.MathExpression -E ifelse(A6>A7,1,0) -V -R 8

If attribute 6 (SENS) is greater than attribute 7 (TIME), then insert a 1 otherwise a 0.

NumericToNominal

With the NumericToNominal filter we will turn the numeric indicators in our comparison attribute into nominal labels:

weka.filters.unsupervised.attribute.NumericToNominal -R 8

Bonus

If you want to use the labels gain/loss instead of 1/0, then you can add the RenameNominalValues filter at the end of the pipeline.

Can one make a C# attribute that is a combination of other attributes that get picked up by Entity Framework Code first migrations

The solution here is relatively simple, and one of my favourite features of the Entity Framework:

Code First Conventions


See Custom Code First Conventions for a full run through, the concept is that you can define your own arbitrary rules or conventions that the EF runtime should obey, this might be based on attributes but it doesn't have to be. You could create a convention based on the suffix or prefix on a field name if you really wanted.

Custom Conventions is a similar mechanism to Custom Type Descriptors as explained in this solution https://stackoverflow.com/a/38504552/1690217, except specifically for Entity Framework Code First models

You were on the right track, making custom attributes simplifies the implementation of custom code conventions, Display attribute however is problematic... Normally I would advise inheriting from the attribute that provides the most configuration, in this case DisplayAttribute, but we cannot inherit that type as it is sealed.
I will unfortunately leave DisplayAttribute out of this solution as there are different convention concepts that can be employed at consumer end. This instead shows how you can use a custom attribute to replace multiple DataAnnotation based Attributes.

public CustomerNoAttribute : Attribute {
}

public class CustomerNoConvention : Convention
{
public CustomerNoConvention()
{
this.Properties()
.Where(p => p.GetCustomAttributes(false).OfType<CustomerNoAttribute>().Any()
.Configure(c => c.HasColumnType("nvarchar")
.HasMaxLength(15)
);
}
}

Now to use the custom attribute in your class:

[Table("foo")]
public class foo {

[CustomerNo]
[Display(Name="Customer Identifier")]
public string CustomerNo {get; set;}

}

[Table("bar")]
public class bar {

[CustomerNo]
[Display(Name="Customer Identifier")]
public string CustomerNo {get; set;}

}

Finally, we have to enable the custom convention, we can do this by overriding OnModelCreating in your DbContext class:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(new CustomerNoConvention());
}

An alternate solution to reducing the duplicate entries of multiple attributes and conventions is of course to use inheritance:

public abstract class HasCustomerNo {

[CustomerNo]
[Display(Name="Customer Identifier")]
public string CustomerNo {get; set;}

}
[Table("foo")]
public class foo : HasCustomerNo {

// no need for CustomerNo

}

[Table("bar")]
public class bar : HasCustomerNo {

// no need for CustomerNo

}


Related Topics



Leave a reply



Submit