In this post we will revisit the PizzaStore demo code from my earlier post. This time to take a closer look at the CodeOnly features. In time for the seminar the feature package was targeted for beta 1. This time around we will be using the Features CTP 2 for beta2 of Visual Studio 2010 / .net 4.0.

Note, this is still a demo only example, to demonstrate some of the CodeOnly features.

In this post we will setup an context/model and a database without using the designer or a .edmx file. This using the ContextBuilder in the feature package.

We are going to need our context, POCO classes and some configuration to use with the ContextBuilder.

But before we start, a quick look at our old demo model the way it looks in the designer:

 

codeonly_post_1

 

We would like to create the equivalent to this model and a database to store it without using the designer. What I’ve done to demonstrate this is a test located in the PizzaStoreTests project in the solution. There are two more projects in the solution. PizzaStore.Data project is home for our PizzaStoreContext class, and the PizzaStore.Model project is home of our POCO classes. Nothing new here compared to earlier demo other than that the Data project doesn’t have a model(.edmx) file.

So to the test located in PizzaStoreTests.cs in the test project.  First off, we start of by create a ContextBuilder for our PizzaStoreContext.

   1: var builder = new ContextBuilder<PizzaStoreContext>();

Configuration

Then we are going to need some configuration to tell the builder about configuration that is not by conventions. This could be done using the builder directly, but we will use separate configuration classes.  The feature package gives us two different base classes to be used for this. EntityConfiguration<TEntity> and ComplexTypeConfiguration<TComplex>. So we create a configuration class for Pizza deriving from EntityConfiguration<Pizza>.

   1: public class PizzaConfiguration : EntityConfiguration<Pizza>
   2: {
   3:   public PizzaConfiguration()
   4:   {
   5:     Property(p => p.Id).IsIdentity();
   6:     Property(p => p.Name).HasMaxLength(50).IsRequired();
   7:     Relationship(p => (ICollection<Topping>)p.Toppings).FromProperty(t => t.Pizzas);
   8:   }
   9: }

In the constructor for this class we add some configuration settings using the base class. First we set that the Id property should be an identity field in the database. Then we set a MaxLenght for the Name property and IsRequired to set it not to accept nulls. Last but not least we configure the many to many relationship with Topping, this using the Relationship method. Here we point out the to properties for this relationship. Not that we need to cast any property that is not exposed as ICollection<T> in the first Relationship to get the right behavior. The fluent way to point the other end of the relationship is done using the FromProperty method.

Back to the test, to add configurations to the build we add to the builds configurations collection.

   1: //Add Configurations        
   2: builder.Configurations.Add(new PizzaConfiguration());

 

We want to add some configuration for the Topping class as well.  This one is a little different be course Topping is using the ComplexType InventoryDetail.

   1: public ToppingConfiguration()
   2: {
   3:     MapSingleType(t => new
   4:     {
   5:         t.Id,
   6:         t.Name,
   7:         ReOrderLevel = t.InventoryDetail.ReOrderLevel,
   8:         UnitsInStock = t.InventoryDetail.UnitsInStock,
   9:         UnitsOnOrder = t.InventoryDetail.UnitsOnOrder
  10:     });
  11:     
  12:     Property(t => t.Id).IsIdentity();
  13:     Property(t => t.Name).HasMaxLength(50).IsRequired();
  14:     Relationship(t => t.Pizzas).FromProperty(p => p.Toppings); 
  15: }

Here we use the MapSingleType method from the base class. This way we could set what column that should map to what property and get to set the column names. We don’t need to do this if we´re happy with the names by conventions. Here we do it to set the column names used by the ComplexType InventoryDetail. If we don’t do this the all columns used by InventoryDetail will be prefixed with “InventoryDetail_”.

The rest of the ToppingConfiguration class is much like the PizzaConfiguration class. In the Test project there is also a configuration class for InventoryDetail, this derives from ComplexTypeConfiguration<TComplex>. But there are no setting for InvetoryDetail that we´re going to need, so this is just include to show how to make a configuration class for complex types.

Test

Now we got all the configuration we need. We add each configuration to the builders configurations collection. Then we create a context using the builders create method using a connection string.

   1: //Add Configurations
   2: builder.Configurations.Add(new PizzaConfiguration());
   3: builder.Configurations.Add(new ToppingConfiguration());
   4: builder.Configurations.Add(new InventoryDetailConfiguration());
   5:  
   6: //Set connection string
   7: SqlConnection connection = new SqlConnection(@"Data Source=.\SQLEXPRESS;Initial Catalog=PizzaStoreCodeOnly_v2;Integrated Security=SSPI;");
   8:  
   9: using (PizzaStoreContext context = builder.Create(connection))
  10: {
  11:   if (context.DatabaseExists())
  12:   {
  13:       context.DeleteDatabase();
  14:   }
  15:   context.CreateDatabase();

 

Then we check if the database exits, if it does we drop it and the creates a new database. Running this will result in a database looking like this:

codeonly_post_2

Extra – writing an edmx

But that’s not all. In the test there is also code that add some test data, but the interesting part is at the bottom.  The ContextBuilder has a method for outputting an edmx called WriteToEdmx(). In the end of the test we’ll out put an .edmx file to the PizzaStore.Data project. So we’ll be able to include this in our project to have a look at the model that ContextBuilder has built up in code. This is not something necessary but could come in hand for some scenarios. In this case we now could compare the model with the one we looked at in the beginning of this post. So if we run the test and then include the generate .edmx file it look like this:

codeonly_post_1

So we now see that we have built up a model/.edmx file and a DB using the CodeOnly feature. We even generated an .edmx file!

Real world

It’s all good in demo land. But this is not where we will use this things.  One of the things we might want if this was a real world scenario, is to break the relationship between Pizza and Topping in the model, removing the Pizzas collection property from Topping. But we still want the many to many in the database.

At first, this try looks like something to solve it, but it doesn’t set the key right in “Pizza_Toppings”.

   1: Relationship(p => (ICollection<Topping>)p.Toppings).Map(new StoreTableName("Pizza_Toppings", "dbo"), (p, t) => new { PizzaId = p.Id, ToppingId = t.Id });

So in the current CTP the is now way of doing this without properties in both end of the relationship. The team is *considering* some thing like this:

   1: HasMany<Pizza>().WithMany(p => p.Toppings);

Download the sample

Kommentarer (1)
Categories: Entity Framework

Kommentarer

trackback
dotway on 2009-12-23 00:38 Entity Framework CodeOnly - Configuring Inheritance/Hierarchy using MEF

Entity Framework CodeOnly - Configuring Inheritance/Hierarchy using MEF
Kommentarer är stängt