The official source of information on Managed Providers, DataSet & Entity Framework from Microsoft
The information in this post is out of date.
Visit msdn.com/data/ef for the latest information on current and past releases of EF.
For Enum Support in Code First see http://msdn.com/data/hh859576
For Enum Support in the EF Designer see http://msdn.com/data/jj248772
This post will provide an overview of working with enumerated types (“enums”) introduced in the Entity Framework June 2011 CTP. We will start from discussing the general design of the feature then I will show how to define an enumerated type and how to use it in a few most common scenarios.
To be able to use any of the functionality discussed in this post you first need to download and install Entity Framework June 2011 CTP bits. For more details including links installation instructions see the Entity Framework June 2011 CTP release blog post.
Enumerated types are designed to be first class citizen in the Entity Framework. The ultimate goal is to enable them in most places where primitive types can be used. This is not the case in this CTP release (see Limitations in Entity Framework June 2011 CTP section) but we are working on addressing most of these limitations in post-CTP releases. In addition enumerated types are also supported by the designer, code gen and templates that ship with this CTP.
As per MSDN enumerated types in the Common Language Runtime (CLR) are defined as “a distinct type consisting of a set of named constants”. Since enums in the Entity Framework are modeled on CLR enums this definition holds true in the EF (Entity Framework) as well. The EF enum type definitions live in conceptual layer. Similarly to CLR enums the EF enums have underlying type which is one of Edm.SByte, Edm.Byte, Edm.Int16, Edm.Int32 or Edm.Int64 with Edm.Int32 being the default underlying type if none has been specified. Enumeration types can have zero or more members. When defined, each member must have a name and can optionally have a value. If the value for the member is not specified then the value will be calculated based on the value of the previous member (by adding one) or set to 0 if there is no previous member. If the value for a given member is specified it must be in the range of its enum underlying type. It is fine to have values specified only for some members and it is fine to have multiple members with the same name value but you can’t have more than one member with the same name (case matters). There are two main takeaways from this:
The last piece of information is that enumerated types can have “IsFlags” attribute. This attribute indicates if the type can be used as set of flags and is an equivalent of [Flags] attribute you can put on an enum type in your C# or VB.NET program. At the moment this attribute is only used for code generation.
The biggest limitations of Enums as shipped in the CTP are:
Now that we know a bit about enums let’s see how they work in practice. To make things simple, in this walkthrough we will create a model that contains only one entity. The entity will have a few properties – including an enum property. In the walkthrough I will use “Model First” approach since it does not require having a database (note that there is no difference in how the tools in VS work for “Database First” approach). After creating a database we will populate it with some data and write some queries which will involve enum properties and values.
Figure 1. Targeting the in Entity Framework 4.2 June 2011 CTP
- Id of type Int32 (should actually already be created with the entity)</![IF>
- Name of type String </![IF>
- Category of type Int32</![IF>
Figure 2. Changing the type of a property to enum
- Beverage
- Dairy
- Condiments
We will not use this type as flags so let’s leave the IsFlag checkbox unchecked. Figure 3 shows how the type should be defined.
Figure 3. Defining an enum type
This caused a few interesting things to happen. First the type of the property (if the properties of the property are not displayed right click the property and select Properties) is now CategoryType. If you open the Type drop down list you will see that the newly added enum type has been added to the list (by the way, did you notice two new primitive types in the drop down? Yes, Geometry and Geography are new primitive types that ship in CTP and make writing geolocation apps much simpler). You can see that on Figure 4.
Figure 4. The type of the property changed to "CategoryType"
Having enum types in this dropdown allows you changing the type of any non-key (CTP limitation) scalar property in your model to this enum type just by selecting it from the drop down list. Another interesting thing is that the type also appeared in the Model Browser, EnumModel à Enum Types node. Note that Enum Types node is new in this CTP and allows defining enum types without having to touch properties – you can just right click it and select Add Enum Type option from the menu. Enum types defined this way will appear in the Type drop down list and therefore can be used for any non-key (CTP limitation) scalar property in the model.
<EntityType Name="Product">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Type="Int32" Name="Id" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
<Property Type="String" Name="Name" Nullable="false" />
<Property Type="EnumModel.CategoryType" Name="Category" Nullable="false" />
</EntityType>
<EnumType Name="CategoryType" UnderlyingType="Byte">
<Member Name="Beverage" />
<Member Name="Dairy" />
<Member Name="Condiments" />
</EnumType>
What you can see in this fragment is the entity type and the enum type we created in the designer. If you look carefully at the Category property you will notice that the type of the property refers to the EnumModel.CategoryType which is our enum type.
1.
[EdmEnumTypeAttribute(NamespaceName="EnumModel", Name="CategoryType")]
[DataContractAttribute()]
public enum CategoryType : byte
{
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EnumMemberAttribute()]
Beverage = 0,
Dairy = 1,
Condiments = 2
}
The underlying enum type is byte – as we chose when defining the type. All the members we defined are present.
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public CategoryType Category
get
return _Category;
set
OnCategoryChanging(value);
ReportPropertyChanging("Category");
_Category = (CategoryType)StructuralObject.SetValidValue((byte)value, "Category");
ReportPropertyChanged("Category");
OnCategoryChanged();
private CategoryType _Category;
partial void OnCategoryChanging(CategoryType value);
partial void OnCategoryChanged();
The code looks very similar to the code generated for primitive properties we know from the previous version of the Entity Framework. The only differences are additional casts required to make enum values work correctly.
So far, so good. The model has been created. It contains an enum type and an entity type with a property of the enum type. Database has been also created. We need to populate the database with some data and will be ready to query the database with queries using enum types.
private static void Seed()
using (var ctx = new EnumModelContainer())
if (!ctx.Products.Any())
ctx.AddToProducts(new Product() {
Name = "Milk", Category = CategoryType.Dairy });
Name = "Seattle's Rain", Category = CategoryType.Beverage });
Name = "Cayenne Pepper", Category = CategoryType.Condiments });
Name = "Sour Cream", Category = CategoryType.Dairy });
Name = "Chevy Volt", Category = (CategoryType)255 });
ctx.SaveChanges();
The method is pretty straightforward. It checks whether there are any entities in the database and if there aren’t it adds some default entities. Certainly, when creating entities we are able to use the members of the enum type we defined in the model. Interestingly however C# and VB.NET allow for kind of abusing enum types by allowing specifying a value that does not correspond to any of the specified members of the enum type (internally we call such a value “unnamed enum value/member”). This is what happens with the last entity – we did not have a category for this product so we used a number that is in range of the enum underlying type and cast it to the actual enum type
private static void DisplayProducts(IEnumerable<Product> products)
Console.WriteLine("Products");
foreach (var p in products)
Console.WriteLine("Id: {0}, Name: {1}, Category: {2}", p.Id, p.Name, p.Category);
static void Main(string[] args)
Seed();
DisplayProducts(ctx.Products);
Products Id: 1, Name: Milk, Category: Dairy Id: 2, Name: Seattle's Rain, Category: Beverage Id: 3, Name: Cayenne Pepper, Category: Condiments Id: 4, Name: Sour Cream, Category: Dairy Id: 5, Name: Chevy Volt, Category: 255 Press any key to continue . . .
So, we were able to create entities with enum properties save them to database and then read them from the database (i.e. round tripping seems to work) and show to the user. In addition we were able to use a value that did not have any corresponding enum type member and nothing blew up. Nice. But this is not actually where enums really shine. The place where enums really shine are queries. It is much more natural to say “Get me all yellow sports cars I own” than “Get me all my sports cars whose color is 6”. So let’s do some queries.
var q = from p in ctx.Products
where p.Category == CategoryType.Dairy
select p;
DisplayProducts(q);
Here is the result:
Products Id: 1, Name: Milk, Category: Dairy Id: 4, Name: Sour Cream, Category: Dairy Press any key to continue . . .
orderby p.Category descending
The result is:
Products Id: 5, Name: Chevy Volt, Category: 255 Id: 3, Name: Cayenne Pepper, Category: Condiments Id: 4, Name: Sour Cream, Category: Dairy Id: 1, Name: Milk, Category: Dairy Id: 2, Name: Seattle's Rain, Category: Beverage Press any key to continue . . .
Using enum values in queries is very natural – queries are much easier to write and to understand. Moreover using enums does not change how queries work – we translate enum properties to corresponding database columns and enum values to corresponding values of the enum underlying type. As a result queries containing enum values or properties work the same way as queries containing only primitive properties as values – everything is translated to SQL and pushed to the database.
The above examples are just the most common scenarios where enum types can be used in EF. However enum types are also supported as properties on complex types, in ESQL queries, as parameters or return types of stored procedures or in TVFs (Table Valued Functions) – another new feature which ships with this CTP. Finally the CTP includes also an updated version of EF 4.1 that supports enums, so you are able to use enums with CodeFirst. We will cover how to use Enums with Code First in an upcoming blog post.
In the walkthrough we went through the process of creating and using enumerated types in the Entity Framework June 2011 CTP1. First we looked at a high level overview of the design of enum types in the EF. Then we created a model with an entity that contained a property of enum type. We inspected edmx file and the generated code for changes related to enums. From the model we prepared we generated a database. Finally, we added entities with enum properties to the database and used enum values when querying the database with LINQ.
As always we appreciate any feedback you may have on enumerated types in the Entity Framework or Entity Framework in general. For support please use the Entity Framework Pre-Release Forum
Pawel Kadluczka, Entity Framework Developer.