This question already has answers here:
Change custom attribute's parameter at runtime
(2 answers)
Closed 3 years ago.
I have a datamembers where order values are mentioned. i want to modify the value according to the order of parameters present in select query. I am unable to set the order value for the datamember at runtime.
Below is the code i tried :
[DataContract]
public class Details
{
[DataMember(EmitDefaultValue = false, Order = 1)]
public string id;
[DataMember(EmitDefaultValue = false, Order = 1)]
public string name;
[DataMember(EmitDefaultValue = false, Order = 1)]
public string creator;
[DataMember(EmitDefaultValue = false, Order = 1)]
public string format;
[DataMember(EmitDefaultValue = false, Order = 1)]
public string creationTime;
}
Type type = executing.GetType("Details");
FieldInfo[] properties = type.GetFields();
properties[0].GetCustomAttributes(typeof(DataMemberAttribute), true).SetValue(2, 3);
I tried the above code to get custom attribute and set value, but its not working.
Is it possible to change attribute values during runtime?
Unfortunately, there is no way to change the value of the Order parameter at runtime. Attributes are already evaluated and integrated in the compiled code at build time so that you can only provide constant values.
The most generic way would be to change the serialization code so that the properties are serialized in the requested order, but this is way too much effort and risk for just adjusting the order, at least imho.
However, what you could do if you have a very limited set of possible queries to create separate methods for each variation and have different classes for the return value that adjust the values as required. One possible approach would be to create a base class for all objects. First, you'd have to use properties instead of fields in your base class. In addition, the keyword virtual prepares the properties for being overriden in a derived class:
[DataContract]
public class Details
{
[DataMember(EmitDefaultValue = false, Order = 1)]
public virtual string id { get; set; }
[DataMember(EmitDefaultValue = false, Order = 1)]
public virtual string name { get; set; }
[DataMember(EmitDefaultValue = false, Order = 1)]
public virtual string creator { get; set; }
[DataMember(EmitDefaultValue = false, Order = 1)]
public virtual string format { get; set; }
[DataMember(EmitDefaultValue = false, Order = 1)]
public virtual string creationTime { get; set; }
}
For each variant, you'd create a derived class, like:
[DataContract]
public class DetailsVariantA : Details
{
[DataMember(EmitDefaultValue = false, Order = 5)]
public override string id { get; set; }
[DataMember(EmitDefaultValue = false, Order = 4)]
public override string name { get; set; }
[DataMember(EmitDefaultValue = false, Order = 3)]
public override string creator { get; set; }
[DataMember(EmitDefaultValue = false, Order = 2)]
public override string format { get; set; }
[DataMember(EmitDefaultValue = false, Order = 1)]
public override string creationTime { get; set; }
}
The method for variant A would return an object of type DetailsVariantA instead of Details.
A word of caution: as you can see in the sample, this approach also involves a lot of extra code because you'd have another class per variant. Also, it introduces the risk that later on, someone forgets to add the properties to all derived classes and so on.
As WCF is primarily used to exchange data between machines and the data is usually not read by humans, from my point of view, I'd not invest the effort and introduce this risk for just changing the order.
Related
I have a domain model to represent a "Resource". This is used by rest API endpoints for CRUD operations.
public class ResourceDto
{
[ScaffoldColumn(false)]
public int ResourceId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string ResourceTypeName { get; set; }
public string ResourceStatusName { get; set; }
public static implicit operator ResourceModel(ResourceDto resource)
{
return new ResourceModel()
{
FirstName = resource.FirstName,
LastName = resource.LastName,
ResourceType = new ResourceTypeModel()
{
Name = resource.ResourceTypeName
},
ResourceStatus = new ResourceStatusModel()
{
StatusName = resource.ResourceStatusName
}
};
}
}
I want to hide the ResourceId property in the case of the Create endpoint because it is a auto generated value in the database. In this example I don't want them sending a value for ResourceId.
But they will need to get the Id coming back from a Read so they can supply it for an Update or Delete.
I've looked at the json method ShouldSerialize but that will only hide it coming out from the Domain to the end point.
I've tried data annotations. [ScaffoldColumn(false)] didn't seem to do anything and [JsonIgnore] hides it completely.
The only thing I can think of is to create two Resource models, one with the id and one without. If I'm thinking about this the wrong way, I welcome some redirection as well. Thanks.
I want to have below class properties to be displayed in PropertyGrid not in the declared order, instead specified attribute? is there such attribute for?
As:
A
B
C
Thanks.
public class ApplicationConfiguration
{
public ApplicationConfiguration()
{
}
public int A { get; set; }
public int C { get; set; }
public int B { get; set; }
}
If you are sending this object from an MVC/WCF application you can use DataMember attribute like below
public class ApplicationConfiguration
{
public ApplicationConfiguration()
{
}
[DataMember(Order=1)]
public int A { get; set; }
[DataMember(Order = 3)]
public int C { get; set; }
[DataMember(Order = 2)]
public int B { get; set; }
}
You have many options in deciding how instances of your classes should appear in the property grid. Start off with Design-Time Attributes for Components and go from there.
See Extending Design-Time Support for the big picture. The bottom line is that you can easily cause your properties to display grouped by categories just by adding [Category("categoryName")] attributes. But if you need them to appear in a completely different order from their order of declaration, then you need to create a Designer.
I have several DataContracts that looks similar to this (shortened for brevity):
[DataContract(Name = "ItemDTO", Namespace = "http://foo.com/")]
public class ItemDTO : IExtensibleDataObject
{
[DataMember(IsRequired = true)]
public string Name { get; set; }
[DataMember]
public string Value { get; set; }
[DataMember(IsRequired = true)]
public int Id { get; set; }
public ExtensionDataObject ExtensionData { get; set; }
}
I hadn't taken notice of the serialized messages before but after a recent change, two things were done: I added a new property, called ReturnCode, and ran CodeMaid's "Reorganize", which alphabetized the properties.
It now looked something like this:
[DataContract(Name = "ItemDTO", Namespace = "http://foo.com/")]
public class ItemDTO : IExtensibleDataObject
{
public ExtensionDataObject ExtensionData { get; set; }
[DataMember(IsRequired = true)]
public int Id { get; set; }
[DataMember(IsRequired = true)]
public string Name { get; set; }
[DataMember]
public int ReturnCode { get; set; }
[DataMember]
public string Value { get; set; }
}
According to Microsoft's page on Data Contract Member Order I realized ReturnCode would break the contract since the serializer would insert it before Value, so I added an Order attribute value, assuming the original order was alphabetic, yielding:
[DataContract(Name = "ItemDTO", Namespace = "http://foo.com/")]
public class ItemDTO : IExtensibleDataObject
{
public ExtensionDataObject ExtensionData { get; set; }
[DataMember(IsRequired = true, Order = 0)]
public int Id { get; set; }
[DataMember(IsRequired = true, Order = 1)]
public string Name { get; set; }
[DataMember(Order = 3)]
public int ReturnCode { get; set; }
[DataMember(Order = 2)]
public string Value { get; set; }
}
This however threw an exception that the deserialized members were out of order. I rolled back to a prior changeset, before all the changes, and sure enough the original order of the members was not alphabetic in the SOAP request (viewed through Fiddler), but following the original order expressed in the code, ie: Name, Value, Id.
I'm currently in the process of adding Order values to all my old DTO types to sequence them according to their prior, pre-alphabetizing of the properties, arrangement. What I'd like to know is why the coded order instead of alphabetized order was being used by the serializer? Microsoft's rules say:
Next in order are the current type’s data members that do not have the
Order property of the DataMemberAttribute attribute set, in
alphabetical order.
Update:
After I added the Order values to sequence the properties in their original order, I again ran Fiddler and it's still using the order the items are literally coded in. In other words, for some reason, my WCF service is completely ignoring any serialization sequencing logic and just sequencing the properties by the order they appear in the .cs file. In fact, the only way I was able to get it to serialize properly was to physically rearrange the properties in each type to their original order. That worked, but it's not preferred.
Update 2 - Solution:
Following Dracor's advice, I added [XmlElement(Order = 1)] attributes and an XmlRootAttribute to my DTOs. The SOAP serialization DID end up following the ordering assigned by these attributes. I hadn't considered it but my service does use Castle DynamicProxy and so I'm guessing it's changing the serializer from DataContractSerializer to XmlSerializer.
Why don't you simply use XmlSerializer to Serialize/Deserialize your XML? It's way more forgiving than DataContractSerializer, and works most of the time.
I am looking for valid architectural solution. For example, in my program exist 'Country' class. This class can be marked with various number of attributes.
[Table(Name="tblCountries")]
[XmlType(Namespace = "bills")]
public class Country
{
[Column(Name = "idCountry", IsPrimaryKey = true, IsDbGenerated = true)]
public int IdCountry { get; set; }
[Column(Name="code")]
[Required(ErrorMessage = "myMessage")]
public string Code { get; set; }
[Column(Name = "title")]
[Required(ErrorMessage = "myMessage")]
public string Title { get; set; }
}
So, is it ok when class and fields are marked by attributes from different spheres? Maybe there is more interesting solution?
You can mark a class or member by any number of different valid attributes you want to.
This just adds meta data to the decorated members - the data will be used by different tools, but shouldn't interfere with your objects.
I have a TrackLog that has a collection of TrackPoints:
public class TrackLog
{
public string Name { get; set; }
public ISet<TrackPoint> TrackPoints { get; set; }
}
public class TrackPoint
{
public DateTime Timestamp { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
}
I'd like to map the track points as a collection of components, as this makes the most sense. According to the book NHibernate in Action, on page 187:
Collections of components are mapped similarily to other collections of value type instances. The only difference is the use of <composite-element> in place of the familiar <element> tag.
How would I do this using Castle ActiveRecord attributes?
Solution:
To expand on Mauricio's answer, the correct mapping is like so:
[ActiveRecord]
public class TrackLog
{
[Property]
public string Name { get; set; }
[HasMany(DependentObjects = true,
MapType = typeof(TrackPoint),
Table = "TrackPoint",
ColumnKey = "TrackLog_Id"
)]
public ISet<TrackPoint> TrackPoints { get; set; }
}
You must provide the MapType, Table, and ColumnKey properties in addition to DependentObjects.
MapType: the type of the class you want to map to
Table: name of the table in the database for the component list
ColumnKey: name of the foreign key column used to relate the child to the parent
Update:
You cannot use Set as the RelationType. It will cause NHibernate to exhibit some weird behavior, where it saves the entities, deletes them, and the re-saves two copies of each element. In the case of my TrackLog, it saved 25 TrackPoints, deleted them, then saved the 25 TrackPoints again two times, for a total of 50. Then when the SessionScope was disposed, it saved another 25, for a total of 75 TrackPoints instead of the expected 25. I couldn't find out the source of this problem, but the fix is to avoid using Set and use Bag (or something else, I only tested it with Bag) instead.
Use DependentObjects = true, e.g.:
public class TrackLog {
[Property]
public string Name { get; set; }
[HasMany(DependentObjects = true)]
public ISet<TrackPoint> TrackPoints { get; set; }
}