Entity Framework Enum and string association - c#

I want to use the method in this article to implement friendlier ToString() outputs for my enum types. I would like to know how this can be done in Entity Framework's auto generated Enum codes? Would I have to modify the code generation template (if so, can someone kindly give me some guidance since the template is rather complicated), or can someone suggest an alternative method?
Thanks!

You can use your own enum type in your EF model, instead of creating a new enum in the model designer. Here's how:
In the model designer, rght click on surface and select:
Add New -> Enum Type...
In the dialog, just set checkbox:
Reference external type
and enter your type: namespace.MyEnum
Then create columns in your tables to use this type.
Since you're likely going to modify the existing model, make sure there is no confusion between enum type from the model and (external) enum type from your code. Best approach would be to remove the enum type you previously had created in the model and adjust the columns to use the associated enum type from your code.
Now, you can declare your enum type with description attributes as you planned.

You don't need to make workarounds for enums. They're supported in the latest Entity Framework.
To make your enums friendly to your website you can use attributes. Here is sample attribute:
public class EnumDescription : Attribute
{
public string Text { get; private set; }
public EnumDescription(string text)
{
this.Text = text;
}
}
Mark your enums with attribute:
public enum DaylightSavingTime
{
[EnumDescription("Detect automatically")]
Auto = 0,
[EnumDescription("DST always on")]
AlwaysOn = 1,
[EnumDescription("DST always off")]
AlwaysOff = 2
}
Add extensions to enable ToDescription() method:
public static class EnumExtensions
{
public static string ToDescription(this Enum enumeration)
{
Type type = enumeration.GetType();
MemberInfo[] memInfo = type.GetMember(enumeration.ToString());
if (null != memInfo && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(EnumDescription), false);
if (null != attrs && attrs.Length > 0)
return ((EnumDescription)attrs[0]).Text;
}
return enumeration.ToString();
}
}
Usage:
var blabla = DaylightSavingTime.Auto;
Console.WriteLine(blabla.ToDescription());
Output:
Detect automatically

Related

Simplify Attribute decorator on methods when referencing multiple libraries [duplicate]

On a control I am using multiple attribute properties:
[Browsable(false)]
[Bindable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Obsolete("", true)]
public new Boolean AllowDrop;
I am using those properties on a lot of the other control properties as well.
I am wondering if there is a way to reduce the amount of code to write each time.
It would be nice if I could combine multiple attributes like this:
[Hidden(true)]
public new Boolean AllowDrop;
Where the Hidden Property would include all the attributes above. So there is only 1 single line of code.
Maybe there is also a way to combine the attributes in a macro or something?
I am aware that there are other ways of hiding properties but I chose the way of using attributes.
Thanks
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.
The best way for me to do this, is by using Metalama (modern rewrite of PostSharp for new .NET releases).
It is absolutely the best framework for doing AOP in .NET from the same guys that did PostSharp. It is still in preview, but Metalama 1.0 will be released in a week or 2, and in next year, it will probably get most of features found in PostSharp... And it has a nice community on Slack and the authors of this Metalama framework are super supportive, they helped me with each question I had, and I had a lot of them already :D
And so this library is perfect for creating custom aspects, but could easily be used for this merging of attributes :) It will be even better then the approach above, because once you see transformed file (using Metalama Diff Preview - you gotta install Metalama extension to VS), then you will actually see all those original attributes there, in a transformed file :)
And so this is how easily I will merge 3 attributes into 1 with Metalama:
Metalama easy merging of attributes 1
(This would be for aspect attributes created by Metalama)
Or, for other attributes (from other libraries), that don't need to do the aspect work, it would by like this: (And this is probably what you want to use, not the first example...):

C# Entity Framework Custom Constraint

I am currently trying to make a custom constraint with the C# Entity Framework. In more detail:
I have a number field which only can have certain values (for example 1, 2 and 3). How do I achieve this constraint in a code first environment?
Entity Framework automatically validates any validations you add to your model by ValidationAttributes. RequiredAttribute or RangeAttribute are two examples of built-in subclasses of this attribute.
If you want some custom validation, the most convenient way is to piggyback on this mechanism and create you own ValidationAttribute subclass.
If you want to validate a non-contiguous range of values you can't use RangeAttribute but you could make an own attribute, for instance like this:
public class AllowedValuesAttribute : ValidationAttribute
{
private readonly ICollection<object> _validValues;
private readonly Type _type;
public AllowedValuesAttribute(Type type, object[] validValues)
{
_type = type;
_validValues = validValues.Where(v => v != null && v.GetType() == type).ToList();
}
public override bool IsValid(object value)
{
return value.GetType() == _type && _validValues.Contains(value);
}
public override string FormatErrorMessage(string name)
{
return string.Format("Value for '{0}' is not an allowed value", name);
}
}
Usage:
[AllowedValues(typeof(int), new object[] { 1, 2, 4, 8, 16 })]
public int Base { get; set; }
Note that we have to use fixed values here, because the content of the attribute must be known at compile time. Also, we have to use object because (currently) C# doesn't support generic attributes. Apart from that, there are numerous options. For example, the attribute could also have a method that finds allowed values at runtime, maybe from a named source, so you can supply this name in its constructor.
I don't see any problem in adorning entity classes with validation attributes. The entity model is not a domain model, it's part of a data access layer. It's primary purpose is (and should be) to facilitate an application's data access. If an entity model also happens to support business logic that's a mere bonus.
It's very bad practice to add dataannotations in domain-model, like D.Mac wrote.
So what about doing it the nicer way?
public MyClass
{
private int myNumberField;
public int MyNumberField
{
get { return myNumberField; }
set
{
if (value >= 1 && value <=3)
myNumberField = value;
else
// throw exception?
// set default-value (maybe 1)?
// do nothing?
}
}
}
you could do whatever you want in the setter of your property
and only restricting it in the front-end is not the best solution, since you always can modify javascript/html - but you should show the user, that he only can insert values 1, 2 or 3.
Also restrict it in the viewmodel with data annotations.
OR:
you could also override EntityFrameworks SaveChanges and add your businesslogic:
public override int SaveChanges(SaveOptions options)
{
foreach (ObjectStateEntry entry in
ObjectStateManager.GetObjectStateEntries(
EntityState.Added | EntityState.Modified))
{
// Validate the objects in the Added and Modified state
// if the validation fails, e.g. throw an exeption.
}
return base.SaveChanges(options);
}

Custom Value Types in Entity Framework

I'd like to use a custom value type as detailed here http://vbcity.com/blogs/jatkinson/archive/2010/01/12/create-custom-types-and-initialize-them-without-the-new-keyword-c-vb-net.aspx]1 in Entity Framework. They underlying type is an enum so I'd like to store the whole thing in the database as an int, the enum's base type. The purpose of the custom value type is for retrieving string values from a dictionary that's mapped to the enum values. When I use the custom value type in EF code first as a property of an entity the database column is not generated. Also, when looking at the model with EF power tools that property does not show up.
Here's my custom value type:
public struct AttachmentType
{
private AttachmentType(AttachmentTypeCode attachmentTypeCode)
{
if (CodesValues.ContainsKey(attachmentTypeCode))
{
_val = attachmentTypeCode;
}
else
{
throw new InvalidEnumArgumentException("This is not a valid attachment type code.");
}
}
public override string ToString()
{
return CodesValues[_val];
}
private AttachmentTypeCode _val;
public static implicit operator AttachmentType(AttachmentTypeCode attachmentTypeCode)
{
return new AttachmentType(attachmentTypeCode);
}
private static readonly Dictionary<AttachmentTypeCode, string> CodesValues = new Dictionary<AttachmentTypeCode, string>()
{
{AttachmentTypeCode.Email, "Electronic Mail Message"},
{AttachmentTypeCode.WordDocument, "Microsoft Word 2007 Document"},
{AttachmentTypeCode.PDF, "Adobe PDF Document"},
};
public enum AttachmentTypeCode
{
Email= 1,
WordDocument= 2,
PDF = 3
}
}
The answer to this problem is to treat the class as a complex type via annotation or Fluent API, add a public property with a getter of the enum type to access the internal private property, and add a custom code-first configuration like the following to map the public property just created (in my case an enum, which EF5 supports) of the complex type to a database field:
modelBuilder.Types<ClassAttachmentTypeIsUsedIn>()
.Configure(ctc => ctc.Property(cust => cust.AttachmentType.EnumProperty)
.HasColumnName("AttachmentType"));
see more here:
http://visualstudiomagazine.com/articles/2014/04/01/making-complex-types-useful.aspx

Can you access a long description for a specific enum value

I usually access enum description for a corresponding value like:
Enum.GetName(typeof(MyEnum), myid);
I need to have an enum that could use any chars like "hello world %^$£%&"
I've seen people attaching an attribute and adding extensions like here:
http://weblogs.asp.net/stefansedich/archive/2008/03/12/enum-with-string-values-in-c.aspx
but I can't work out if this can be used to access the long description.
Anyone done anything similar?
Thanks
Davy
Why can't it work out?
You can create your own attribute by inherting from Attribute
public class EnumInformation: Attribute
{
public string LongDescription { get; set; }
public string ShortDescription { get; set; }
}
public static string GetLongDescription(this Enum value)
{
// Get the type
Type type = value.GetType();
// Get fieldinfo for this type
FieldInfo fieldInfo = type.GetField(value.ToString());
// Get the stringvalue attributes
EnumInformation [] attribs = fieldInfo.GetCustomAttributes(
typeof(EnumInformation ), false) as EnumInformation [];
// Return the first if there was a match.
return attribs != null && attribs.Length > 0 ? attribs[0].LongDescription : null;
}
public static string GetShortDescription(this Enum value)
{
// Get the type
Type type = value.GetType();
// Get fieldinfo for this type
FieldInfo fieldInfo = type.GetField(value.ToString());
// Get the stringvalue attributes
EnumInformation [] attribs = fieldInfo.GetCustomAttributes(
typeof(EnumInformation ), false) as EnumInformation [];
// Return the first if there was a match.
return attribs != null && attribs.Length > 0 ? attribs[0].ShortDescription : null;
}
Your Enum would look like this
public enum MyEnum
{
[EnumInformation(LongDescription="This is the Number 1", ShortDescription= "1")]
One,
[EnumInformation(LongDescription = "This is the Number Two", ShortDescription = "2")]
Two
}
You can use it this way
MyEnum test1 = MyEnum.One;
Console.WriteLine("test1.GetLongDescription = {0}", test1.GetLongDescription());
Console.WriteLine("test1.GetShortDescription = {0}", test1.GetShortDescription());
It outputs
test1.GetLongDescription = This is the Number 1
test1.GetShortDescription = 1
You can actually add properties to the attribute to have all kinds of information. Then you could support the localization you are looking for.
What do you mean by "long description"? I've got a library which allows you to attach Description attributes to enum values and fetch them:
public enum Foo
{
[Description("This is a really nice piece of text")]
FirstValue,
[Description("Short but sweet")]
Second,
}
If you're talking about the XML documentation, that's a different matter - that doesn't get built into the binary, so you'd have to build/ship the XML as well, and then fetch it at execution time. That's doable, but I don't have code to do it offhand...
I tend to stay away from this practice. If you think about it, it's binding your code's logic to how you typed your code. It would be much better to use a switch statement, resource file, database, etc...
I learned this the hard way. I had an app that we ultimately decided to obfuscate to help secure our code. As you can imagine, our binaries stopped working the way we wanted due to the enums be renamed during the obfuscation.
If you need an easy way to extract a description attribute for an enum value, have a look at my answer to a similar question
You just need to call the GetDescription extension method on the value :
string description = myEnumValue.GetDescription();
The Unconstrained Melody library mentioned by Jon includes a similar extension method (among many other cool things), you should check it out.

Enum with default typecast? is that possible?

is it possible to make a default typecast for an enum?
I use enum for a lot, such as states and I want to compare enums directly to LINQ fields, but I have to typecast all the time.
You should be able to have properties in your LINQ objects that have an enum type. This way you do not have to cast.
So just change your properties to have the correct enum type and you don't have to worry about casts any longer. You can do this in the LINQtoSQL designer. Just right-click on a property, select 'Properties' and set the appropriate Type in the Visual Studio Properties window.
The answer was MUCH more simple!!!
A good friend of mine told me this is very simple! have a look at this sample!
public enum State:byte
{
EmailNotValidated = 0x00,
EmailValidated = 0x10,
Admin_AcceptPending = 0x40,
Active = 0x80,
Admin_BlockedAccount = 0xff
}
Pay attention to the :BYTE part after the name of the Enum... there is the trick I was looking for! But thanks to everyone trying for me!
LINQ-to-SQL will usually handle direct integer maps and exact string (name) maps (note: case sensitive). Meaning: write your enum somewhere, and in the designer set the property type as the fully-qualified enum name: Some.Namespace.MyEnum. It should usually work.
For non-trivial mappings (for example where the column is a varchar with mixed-case values, or things like "In Progress" [note the space]), you will have to leave the storage property as int/varchar (etc) and map it manually. I usually do this by marking it as private and naming it FooStorage, and adding a mapping property in a partial class:
partial class MyType {
public MyEnum Foo {
get {... mapping logic reading from FooStorage...}
set {... mapping logic, updating FooStorage...}
}
}
The only problem is that LINQ queries will only work against the storage property (not the bespoke property).
Have you tried extension methods?
public enum MyEnum
{
First = 1,
Second = 2,
Third = 3
}
public static class Utility
{
public static string Description(this Enum e)
{
Type t = e.GetType();
DescriptionAttribute[] desc =
(DescriptionAttribute[])(t.GetField(e.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false));
return desc.Length > 0 ? desc[0].Description : e.ToString();
}
public static byte ToByte(this Enum ai)
{
object o=Enum.ToObject(ai.GetType(), ai);
return Convert.ToByte(o);
}
}
class Program
{
static void Main(string[] args)
{
MyEnum me = MyEnum.Third;
Console.WriteLine("Value: {0}\r\nType: {1}"
,me.ToByte(),me.ToByte().GetType().ToString());
Console.ReadLine();
}
}
It outputs:
Value: 3
Type: System.Byte

Categories

Resources