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.
Related
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.
I would like to use inheritance to enforce consistency in Entity Framework model classes. For example, if X different models all have an address, they could inherit from:
public abstract class EntityAddress
{
[MaxLength(400)]
[Display(Name = "Street address")]
[DataMember]
public string AddressLine1 { get; set; }
[MaxLength(400)]
[Display(Name = "Address line 2")]
[DataMember]
public string AddressLine2 { get; set; }
[MaxLength(100)]
[Display(Name = "City")]
[DataMember]
public string City { get; set; }
[MaxLength(100)]
[Display(Name = "State")]
[DataMember]
public string State { get; set; }
[MaxLength(40)]
[Display(Name = "Zip code")]
[DataType(DataType.PostalCode)]
[DataMember]
public string ZipCode { get; set; }
}
This would ensure that all addresses are consistently implemented across the product (yes, if a model has two addresses, we have an issue, but I'll wave that away for the purposes of this discussion).
I would also like the ability to have a class use an unlimited number of these concepts. For example, if a model has an address and a full name, it could do this:
public class Customer : EntityAddress, EntityFullName
{
}
Multiple inheritance, however, is not supported in C#.
Does anyone have any ideas on good alternate methods to achieve what I am trying to do here? I don't believe interfaces will work because I can't embed the attributes with the properties. I don't believe a class property will work because I want the columns in the DB associated with the base classes to be in the same table as the model class properties.
Complex types appear to be an answer to this question (credit to Ivan Stoev).
https://weblogs.asp.net/manavi/associations-in-ef-4-1-code-first-part-2-complex-types
You could wrap in a class. Its not pretty, but it will achieve similar results
public class Customer
{
public EntityAddress address {get;set;}
public EntityFullName fullname {get;set;}
}
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 have these properties (A,B) :
[DataType(DataType.MultilineText)]
[Required(ErrorMessage = "A is required"), DisplayName("A")]
[StringLength(Constants.MaximunStringSize)]
public string A { get; set; }
[DataType(DataType.MultilineText)]
[Required(ErrorMessage = "B is required"), DisplayName("B")]
[StringLength(Constants.MaximunStringSize)]
public string B { get; set; }
I can create a class that "inherits" all the attributes (DataType, Required, StringLength, DisplayName) and the set through its constructor?. By example:
[MyAttribute("A","A is required")]
public string A { get; set; }
[MyAttribute("B","B is required")]
public string B { get; set; }
In general, no.
However, for validation attributes, you could create your own validation attribute that contains all of the logic in the existing attributes.
To emulate [DataType], you'll need to implement IMetadataAware.
There is no multiple inheritance in C#, so no, you can't do this.
You can, however write your own Attribute that incorporates all the functionality of those attributes.
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; }
}