Archives

Showing posts with label Visual Studio. Show all posts
Showing posts with label Visual Studio. Show all posts

Tuesday, June 14, 2011

Deploying an Entity Data Model

In the past few posts, you've seen how to create and modify an Entity Data Model (EDM). You've also looked at the XML behind the visual EDM to understand how the conceptual model in the application maps to the data in the data store.

You may recall that I mentioned in the first post how easy it is to change your applications data store without having to modify the application itself. So, here's how to do it.

By default, the Entity Data Model Wizard creates a model with the metadata embedded inside the application assembly. If you take a look in App.config or Web.config, you'll see the connection information as shown below.

<connectionStrings>

<add name="AdventureWorksEntities" connectionString="metadata=res://*/AWModel.csdl|res://*/AWModel.ssdl|res://*/AWModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=DemoBox\SQLExpress;Initial Catalog=AdventureWorks;Integrated Security=True;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />

</connectionStrings>

Note that the metadata is identified as being a resource in the assembly by using the res://* path.

This works fine for many applications; however, changes to the model then require changes to the assembly. For example, if your storage model changes (a not uncommon occurrence), you will need to update the model and redeploy the whole assembly. Because it's only likely to be the storage model and mapping sections of the .edmx file that change, not the conceptual model that the application is coded against, it's far easier to store the model externally to the assembly and only update and redeploy the model itself.

The Metadata Artifact Processing property of the model defines where the model is deployed to at build time.

EntityModelProperties

The default setting is Embed in Output Assembly, but you can change this to Copy to Output Directory to store the model outside of the assembly. Now when you build the application, you'll see the .cdsl, .ssdl, and .msl files created in the debug directory.

DebugDirectory

And the connection string is now updated to point to these external files.

<connectionStrings><add name="AdventureWorksEntities" connectionString="metadata=.\AWModel.csdl|.\AWModel.ssdl|.\AWModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=DemoBox\SQLExpress;Initial Catalog=AdventureWorks;Integrated Security=True;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" /></connectionStrings>

So now if you change the model, you can simply rebuild the application and then copy the new model files to the production server without redeploying the assembly.

Read More >>

ADO.NET Entity Data Model Mapping

In my last two posts, I've shown you how to create and modify ADO.NET Entity Data Models (EDMs). Now we'll look at the mappings behind the model and how the information is stored in the model.

When you use the Entity Data Model Wizard to generate a model, Visual Studio automatically displays it in the Entity Designer and stores it as an .edmx file. The view you see in the Entity Designer is a graphical representation of the XML data stored in the .edmx file. This XML comprises of the storage model of the data, the conceptual model, and the mapping specification between the two.

Let's take a look at the XML behind the EDM I created in previous posts.

Storage Model (SSDL)

The storage model is described by using the store schema definition language, or SSDL. The section begins by defining the schema namespace and the provider used to access the data.

<Schema Namespace="AdventureWorksModel.Store" Alias="Self" Provider="System.Data.SqlClient" ProviderManifestToken="2008" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns="http://schemas.microsoft.com/ado/2009/02/edm/ssdl">

It then declares one EntitySet element per entity in the model. This element defines the name, base type, store type, and schema for the entity.

<EntityContainer Name="AdventureWorksModelStoreContainer">

<EntitySet Name="Contact" EntityType="AdventureWorksModel.Store.Contact" store:Type="Tables" Schema="Person" />

<EntitySet Name="Customer" EntityType="AdventureWorksModel.Store.Customer" store:Type="Tables" Schema="Sales" />

<EntitySet Name="SalesOrderHeader" EntityType="AdventureWorksModel.Store.SalesOrderHeader" store:Type="Tables" Schema="Sales" />

<EntitySet Name="SalesTerritory" EntityType="AdventureWorksModel.Store.SalesTerritory" store:Type="Tables" Schema="Sales" />

<EntitySet Name="StoreContact" EntityType="AdventureWorksModel.Store.StoreContact" store:Type="Tables" Schema="Sales" />

The next elements declare the associations between the entities, specifying the entity at either end of the association. For example, here's the SalesOrderHeader to Contact association.

<AssociationSet Name="FK_SalesOrderHeader_Contact_ContactID" Association="AdventureWorksModel.Store.FK_SalesOrderHeader_Contact_ContactID">

<End Role="Contact" EntitySet="Contact" />

<End Role="SalesOrderHeader" EntitySet="SalesOrderHeader" />

</AssociationSet>

Then the entities themselves are defined, with elements for each property of the entity specifying the name, type, nullability, and maximum length of the property. Here's the XML defining the Contact entity.

<EntityType Name="Contact">

<Key>

<PropertyRef Name="ContactID" />

</Key>

<Property Name="ContactID" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />

<Property Name="NameStyle" Type="bit" Nullable="false" />

<Property Name="Title" Type="nvarchar" MaxLength="8" />

<Property Name="FirstName" Type="nvarchar" Nullable="false" MaxLength="50" />

<Property Name="MiddleName" Type="nvarchar" MaxLength="50" />

<Property Name="LastName" Type="nvarchar" Nullable="false" MaxLength="50" />

<Property Name="Suffix" Type="nvarchar" MaxLength="10" />

<Property Name="EmailAddress" Type="nvarchar" MaxLength="50" />

<Property Name="EmailPromotion" Type="int" Nullable="false" />

<Property Name="Phone" Type="nvarchar" MaxLength="25" />

<Property Name="PasswordHash" Type="varchar" Nullable="false" MaxLength="128" />

<Property Name="PasswordSalt" Type="varchar" Nullable="false" MaxLength="10" />

<Property Name="AdditionalContactInfo" Type="xml" />

<Property Name="rowguid" Type="uniqueidentifier" Nullable="false" />

<Property Name="ModifiedDate" Type="datetime" Nullable="false" />

<Property Name="CurrentPoints" Type="int" Nullable="false" />

</EntityType>

And then the associations are defined, including their multiplicity properties and referential constraints. Here's the definition for the SalesOrderHeader to Contact association.

<Association Name="FK_SalesOrderHeader_Contact_ContactID">

<End Role="Contact" Type="AdventureWorksModel.Store.Contact" Multiplicity="1" />

<End Role="SalesOrderHeader" Type="AdventureWorksModel.Store.SalesOrderHeader" Multiplicity="*" />

<ReferentialConstraint>

<Principal Role="Contact">

<PropertyRef Name="ContactID" />

</Principal>

<Dependent Role="SalesOrderHeader">

<PropertyRef Name="ContactID" />

</Dependent>

</ReferentialConstraint>

</Association>

The final section of the SSDL for this model defines the function import that I used to access the stored procedure. It defines the name of the function and the parameters it takes.

<Function Name="Sales_spNumberOfOrdersForAContact" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" StoreFunctionName="Sales.spNumberOfOrdersForAContact" Schema="dbo">

<Parameter Name="ContactID" Type="int" Mode="In" />

</Function>

</Schema>

</edmx:StorageModels>

Conceptual Model (CSDL)

The conceptual model is described by using the conceptual schema definition language, or CSDL. Similarly to the storage model, it begins by declaring the schema namespace for the model.

<edmx:ConceptualModels>

<Schema Namespace="AdventureWorksModel" Alias="Self" xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation" xmlns="http://schemas.microsoft.com/ado/2008/09/edm">

It then declares each EntitySet used in the model.

<EntityContainer Name="AdventureWorksEntities" annotation:LazyLoadingEnabled="true">

<EntitySet Name="Contacts" EntityType="AdventureWorksModel.Contact" />

<EntitySet Name="SalesOrderHeaders" EntityType="AdventureWorksModel.SalesOrderHeader" />

<EntitySet Name="SalesTerritories" EntityType="AdventureWorksModel.SalesTerritory" />

<EntitySet Name="StoreContacts" EntityType="AdventureWorksModel.StoreContact" />

And continues by declaring the associations.

<AssociationSet Name="FK_SalesOrderHeader_Contact_ContactID" Association="AdventureWorksModel.FK_SalesOrderHeader_Contact_ContactID">

<End Role="Contact" EntitySet="Contacts" />

<End Role="SalesOrderHeader" EntitySet="SalesOrderHeaders" />

</AssociationSet>

It then declares the function import and the parameters it uses.

<FunctionImport Name="NumberOfOrders" ReturnType="Collection(Int32)">

<Parameter Name="ContactID" Mode="In" Type="Int32" />

</FunctionImport>

</EntityContainer>

Then each entity is defined in terms of its properties and their attributes.

<EntityType Name="Contact">

<Key>

<PropertyRef Name="ContactID" />

</Key>

<Property Name="ContactID" Type="Int32" Nullable="false" annotation:StoreGeneratedPattern="Identity" />

<Property Name="NameStyle" Type="Boolean" Nullable="false" />

<Property Name="Title" Type="String" MaxLength="8" Unicode="true" FixedLength="false" />

<Property Name="FirstName" Type="String" Nullable="false" MaxLength="50" Unicode="true" FixedLength="false" />

<Property Name="MiddleName" Type="String" MaxLength="50" Unicode="true" FixedLength="false" />

<Property Name="LastName" Type="String" Nullable="false" MaxLength="50" Unicode="true" FixedLength="false" />

<Property Name="Suffix" Type="String" MaxLength="10" Unicode="true" FixedLength="false" />

<Property Name="EmailAddress" Type="String" MaxLength="50" Unicode="true" FixedLength="false" />

<Property Name="EmailPromotion" Type="Int32" Nullable="false" />

<Property Name="Phone" Type="String" MaxLength="25" Unicode="true" FixedLength="false" />

<Property Name="PasswordHash" Type="String" Nullable="false" MaxLength="128" Unicode="false" FixedLength="false" />

<Property Name="PasswordSalt" Type="String" Nullable="false" MaxLength="10" Unicode="false" FixedLength="false" />

<Property Name="AdditionalContactInfo" Type="String" MaxLength="Max" Unicode="true" FixedLength="false" />

<Property Name="rowguid" Type="Guid" Nullable="false" />

<Property Name="ModifiedDate" Type="DateTime" Nullable="false" />

<Property Name="CurrentPoints" Type="Int32" Nullable="false" />

<NavigationProperty Name="SalesOrderHeaders" Relationship="AdventureWorksModel.FK_SalesOrderHeader_Contact_ContactID" FromRole="Contact" ToRole="SalesOrderHeader" />

<NavigationProperty Name="StoreContacts" Relationship="AdventureWorksModel.FK_StoreContact_Contact_ContactID" FromRole="Contact" ToRole="StoreContact" />

</EntityType>

Finally, the associations are defined with their multiplicities and referential constraints.

<Association Name="FK_SalesOrderHeader_Contact_ContactID">

<End Role="Contact" Type="AdventureWorksModel.Contact" Multiplicity="1" />

<End Role="SalesOrderHeader" Type="AdventureWorksModel.SalesOrderHeader" Multiplicity="*" />

<ReferentialConstraint>

<Principal Role="Contact">

<PropertyRef Name="ContactID" />

</Principal>

<Dependent Role="SalesOrderHeader">

<PropertyRef Name="ContactID" />

</Dependent>

</ReferentialConstraint>

</Association>

Conceptual to Storage Mapping (C-S Mapping)

The final section of the file defines the mapping between the conceptual and storage models. It describes how each entity from the store should map to each entity in the conceptual model that the application is written against. Here's the mapping fragment for the Contact entity.

<EntityContainerMapping StorageEntityContainer="AdventureWorksModelStoreContainer" CdmEntityContainer="AdventureWorksEntities">

<EntitySetMapping Name="Contacts">

<EntityTypeMapping TypeName="AdventureWorksModel.Contact">

<MappingFragment StoreEntitySet="Contact">

<ScalarProperty Name="ContactID" ColumnName="ContactID" />

<ScalarProperty Name="NameStyle" ColumnName="NameStyle" />

<ScalarProperty Name="Title" ColumnName="Title" />

<ScalarProperty Name="FirstName" ColumnName="FirstName" />

<ScalarProperty Name="MiddleName" ColumnName="MiddleName" />

<ScalarProperty Name="LastName" ColumnName="LastName" />

<ScalarProperty Name="Suffix" ColumnName="Suffix" />

<ScalarProperty Name="EmailAddress" ColumnName="EmailAddress" />

<ScalarProperty Name="EmailPromotion" ColumnName="EmailPromotion" />

<ScalarProperty Name="Phone" ColumnName="Phone" />

. . .

</MappingFragment>

</EntityTypeMapping>

</EntitySetMapping>

There's a similar mapping for the function import too.

<FunctionImportMapping FunctionImportName="NumberOfOrders" FunctionName="AdventureWorksModel.Store.Sales_spNumberOfOrdersForAContact" />

</EntityContainerMapping>

In these simple examples, the property names in the model match the column names in the data store. However, you can edit property names in the model and this mapping section is where they are linked to the original column name in the data store. Here's how the XML looks if I change the property name from Phone to Cellphone in the Contact entity in the model.

In the SSDL section, the property is still listed as Phone because it is describing the storage model.

<EntityType Name="Contact">

<Key>

<PropertyRef Name="ContactID" />

</Key>

<Property Name="ContactID" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />

. . .

<Property Name="Phone" Type="nvarchar" MaxLength="25" />

. . .

</EntityType>

But in the CSDL section describing the conceptual model, you can see that the property is now called Cellphone.

<EntityType Name="Contact">

<Key>

<PropertyRef Name="ContactID" />

</Key>

<Property Name="ContactID" Type="Int32" Nullable="false" annotation:StoreGeneratedPattern="Identity" />

. . .

<Property Name="Cellphone" Type="String" MaxLength="25" Unicode="true" FixedLength="false" />

. . .

</EntityType>

And in the C-S mapping section, the two differently named properties are mapped together.

<EntitySetMapping Name="Contacts">

<EntityTypeMapping TypeName="AdventureWorksModel.Contact">

<MappingFragment StoreEntitySet="Contact">

<ScalarProperty Name="ContactID" ColumnName="ContactID" />

. . .

<ScalarProperty Name="Cellphone" ColumnName="Phone" />

. . .

</MappingFragment>

</EntityTypeMapping>

</EntitySetMapping>

So you've now seen how the XML behind an EDM defines the storage model from the data store, the conceptual model in the application, and the mappings between the two.

Read More >>

Monday, June 13, 2011

Modifying ADO.NET Entity Data Models

Continuing our look at Entity Data Models (EDMs), you'll now see how to modify an existing model. During the development process, it's not unusual to find that you need to access more data than originally planned. In Visual Studio, you can simply run the Update Wizard to add entities linked to additional tables that you need or define your own custom entities to the model and the database at the same time.

I'll continue working with the model that I created in the previous post, which contains entities for sales territory, sales orders, and store contacts. I now decide that the application needs additional information from the Customer table in the database. The context menu in the Entity Designer window enables you to launch the Update Wizard and select the new tables that you want to use.

EntityDesignerContextMenu

And you can see that the model automatically generates the associations between the new and existing entities.

NewlyAddedEntity

If I want to use data that is not currently stored in my data source, I can define an entity in the model and then update the database to create a table to hold the new information. The Entity Designer context menu enables you to add new items to the model.

NewlyCreatedEntity

I can define the entity and then add properties and associations to it.

EntityProperties

Entity

I can then either map the new entity to existing data in my enterprise or use the Generate Database Wizard to generate the Transact-SQL code necessary to add tables for the entity to my database.

GenerateDatabaseWizard

Note that the Generate Database Wizard generates a script that defines the entire EDM and begins by dropping all the existing tables mapped to the model. If you want to create only a subset of the model, you can use the wizard to generate the script, copy the code from the wizard, and then edit the code to use only the sections that you need.

You can also map entities in the model to stored procedures or views in a database. I've already added a stored procedure named spNumberOfOrdersForAContact to the AdventureWorks database as defined below.

SET ANSI_NULLS ON;
GO

SET QUOTED_IDENTIFIER OFF;
GO

USE [AdventureWorks]
GO

CREATE PROCEDURE [Sales].[spNumberOfOrdersForAContact]
@ContactID [int] = 1
AS
BEGIN
SELECT COUNT(*)
FROM [Sales].[SalesOrderHeader]
WHERE [ContactID] = @ContactID
END;
GO

This stored procedure takes a ContactID as an input and then returns the number of orders that the contact has made as a scalar value.

To access the stored procedure, I first add it to the model by using the Update Wizard similarly to adding a new table to the model and then create a function import in the model to retrieve the data.

FunctionImport

So you've now seen how easy it is to modify models to include additional data in them. In my next post, I'll show you more details about how the model is mapped to the data source.

Read More >>

Creating ADO.NET Entity Data Models

Over the past few years, data access in .NET Framework applications has been transformed. The introduction of the Entity Framework into ADO.NET means that you can use conceptual models of your normalised data, without worrying about the structure of that data in your data store. You simply define the entities that you require in your application and then map them to the underlying data by using an Entity Data Model (EDM). Sounds complex, but by using the Entity Data Model tools in Visual Studio, it's just a case of following a wizard and then a little click & drag. I'll start by looking at how to create a model from an existing database and in the next post I'll show you some of the cooler features of the tools.

If I add a new ADO.NET Entity Data Model item to a project, Visual Studio automatically launches the Entity Data Model Wizard to step through the creation of an EDM. The first decision to make is whether to generate an EDM from an existing database or create an empty model. I'll choose the first option for this post.

EDMWizard1

After selecting or creating a connection to the AdventureWorks database, I can select the database objects that I want to use in my application. I'll simply choose a few tables for now, but you can also create entities from views and stored procedures.

EDMWizard2

When I click Finish, Visual Studio creates an EDM with an entity per table that I selected and associations between the entities corresponding to the relationships defined in the database.

EntityDesigner

If I right-click in the designer pane and click Mapping Details, I can view the mappings between an entity in my EDM and the underlying table in the database.

MappingDetails

And if I close the designer pane and then right-click the model in Solution Explorer, I can select to open it in an XML editor to see the XML code behind the mappings.

XMLMapping

When Visual Studio generates the model and mappings, it also auto-generates an object layer based on the contents of the model.

AdventureWorksEntitiesClass

I can code against these objects and their properties to work with and navigate around the data in the model.

Code

As you can see, no more complex Transact-SQL join query strings risking run-time errors if my table or field names aren't accurate, just simple coding against a conceptual model that maps to my data. These few lines of code populate a data grid with SQL Server data based on the contents of a combo box on a form.

Form

You can create an EDM for many different types of data source, so it makes it easier to update applications if your data store changes. You simply change the connection information for the model, update your mappings, and your code should continue to run without any modifications. If you also configure your application to store the model data externally (use the Metadata Artifact Processing property of the model), you only need to redeploy the three model definition files, not the entire application.

So you've now seen how quick and easy it is to generate a model from an existing database and write code against in. In my next post, I'll show you more features of the Entity Designer and EDMs.

Read More >>

Thursday, December 18, 2008

Domain Specific Languages (VI)

Generating Code from the Model

The DSL will now be tested by producing a model, and developing a T4 template to generate code from the model.
  • In the DSL (running under the Visual Studio Experimental Hive), open Sample.mydsl, and draw a model of a Male class inheriting from a Person class, and a Female class inherting from the same Person class. Set the Visibility property of all classes to public.
  • Open MyDSLSampleReport.tt. In text templates, you indicate directives by using <#@ tags, statements with <# tags, and expressions with <#= tags.
  • Change the output extension directive to .cs.
  • Comment out the generated material line.
  • Change the foreach loop to that shown in the image below. To use code outlining and intellisense, download and install the Clarius T4 Editor. The loop states that for each Class in the ClassModel, display the class Visibility, the word class, the class Name, and then if the class has a BaseClass, to display the BaseClass name after a colon.

  • Save the T4 template and examine the generated code (MyDSLSample.cs). The generated code lists the class visibility, the class name, and the base class (if applicable).
Conclusion

In this series of posts I've shown you how to create and customize a simple Domain Specific Language by using the DSL Tools that are part of the Visual Studio SDK. There’s a lot more to DSLs than covered here, including more extensive customization, adding and using coherence rules, performing model validation both interactively and on request, writing more extensive T4 templates, and deploying a DSL by obtaining and adding the necessary package load keys, and creating a DSL setup project. For more information on these topics, see the VSX website.
Read More >>

Domain Specific Languages (V)

Customizing the Graphical Notation

The DSL graphical notation will now be customized:
  • Rename ExampleShape to ClassShape.
  • Change the Fill Color property of ClassShape to a suitable colour.
  • Change the Geometry property of ClassShape to rounded rectangle (the standard for UML class diagrams).
  • Add a text decorator to ClassShape called Visibility. This is necessary so that the user can enter a visibility level (public, private etc.). To do this, right-click on ClassShape and select Add->Text Decorator.
  • Map the Visibility text decorator to the Visibility property of Class in the DSL Details window. To do this, click the DSL Details window, and select the line that links ClassShape to Class. In the DSL Details window, select the Decorator Maps tab, select the Visibility check box, and then set the Display Property combo box to Visibility. This ensures that when the user enters a class visibility, that it will be stored in the Visibility property of Class. Close the DSL Details window.

  • Rename NameDecorator to Name.
  • Rename ExampleConnector to ClassConnector.
  • Change the Target End Style property of ClassConnector to Hollow Arrow (the standard for UML class diagrams).

After making these changes, your domain model should look like:

Customizing the DSL Toolbox

  • In DslExplorer, expand Editor/Toolbox Tabs/MyDslSample/Tools.
  • Change the Name property of ExampleElement to Class.
  • Change the ToolTip property of ExampleElement to “Creates a Class”.
  • Change the Name property of ExampleRelationship to Inheritance.
  • Change the ToolTip property of ExampleRelationship to “Drag between Classes to create an Inheritance relationship”.

Click the Transform All Templates button. Once the template transformation process has finished, build the application and run it without debugging. The DSL will launch in the Visual Studio Experimental Hive.
Read More >>

Domain Specific Languages (IV)

Customizing the Domain Model

In order to produce a DSL for modelling simple inheritance relationships, the minimal language domain model produced by the wizard will have to be customized. This will involve renaming the name of the domain classes and domain relationships, adding a new domain property to store the class visibility (public/private etc.), and adding a textual decorator to display the visibility property:
  • Rename ExampleModel to ClassModel.
  • Rename the ExampleElement domain class to Class.
  • Rename the Elements role to Classes.
  • Rename the ClassReferencesTargets relationship to Inheritance.
  • Rename the Targets role to BaseClass.
  • Change the Multiplicity property of BaseClass to 0..1.
  • Rename the Sources role to DerivedClasses.
  • Add a domain property to Class, named Visibility of type String (will enable the user to specify the visibility of the class). To do this, right-click on Class and select Add->DomainProperty.
After making these changes, your domain model should look like:

Finally, click the Transform All Templates button.


Template transformation should always be performed when anything changes in the model, so that the generated code reflects the domain classes and domain relationships on the diagram.
Read More >>

Domain Specific Languages (III)

Understanding the Domain Specific Language

The generated graphical DSL includes the following features:
  • Domain Model: a DSL is defined by its domain model. The domain model includes the domain classes and domain relationships that form the basis of the DSL. The domain model is not the same as a model. The domain model is the design-time representation of the DSL, while the model is the run-time instantiation of the DSL. Domain classes are used to create the various elements in the domain, and domain relationships are the links between the elements. The domain model can be seen in the Classes and Relationships swimlane of the image below.

    Every domain relationship has two roles: a source role and a target role. The Name property of a role is the name that is used to navigate from a domain relationship to one of the domain classes that it connects. The PropertyName property of a role is used to navigate from an element at one end of the relationship to the element or elements at the opposite end of the relationship. The DisplayName property of a role is used in the DSL Explorer to show relationships between elements. By default, the value of the DisplayName property is the same as that of the Name property.

    Multiplicities specify how many elements can have the same role in a domain relationship. In most cases, the number of relationships that are connected to a given element is the same as the number of elements that are connected through those relationships. In the diagram, the zero-to-many (0..*) multiplicity setting on the Elements role specifies that any instance of the ExampleModel domain class can have as many ExampleModelHasElements relationship links as you want to give it. A multiplicity of one (1..1) on a role specifies that each instance of the domain class can have only one link in the relationship.


  • Graphical Notation: a DSL has a set of elements that can be easily defined and extended to represent domain-specific constructs. A graphical notation consists of shapes, which represent the elements, and connectors, which represent the relationships between elements, on a graphical diagram surface. The graphical notation can be seen in the Diagram Elements swimlane in the image above.
  • Artifact Generators: one of the main purposes of a DSL is to generate an artefact, for example, source code. A change in the model typically results in a change in the artifact. Here, artifacts are generated from T4 files (file extension .tt). In the Dsl project, look at the T4 files in the GeneratedCode folder, and the corresponding C# files they generate.

  • Serialization: a DSL must be persisted in a format that can be edited, saved and reloaded. DSL Tools use an XML format.
Read More >>

Domain Specific Languages (II)

Creating a Domain Specific Language

If you've installed the Visual Studio 2008 SDK, there will be some new project templates available for you to use when you choose New Project in Visual Studio. Select the Domain-Specific Language Designer project type, which can be found underneath Other Project Types and Extensibility. Choose a name for your project (MyDslSample), make sure it will be saved in a suitable location and then click OK. The Domain-Specific Language Designer wizard will appear and guide you through the process of creating a DSL solution.

The first step is to choose one of the solution templates. By choosing the template that most closely resembles the language that you want to create, you can minimize the modifications that you have to make to the starting solution. The solution templates that are available are:
  • Class Diagrams: use this template if your DSL includes entities and relationships that have properties. This template creates a DSL that resembles UML class diagrams.
  • Component Models: use this template if your DSL includes components, that is, parts of a software system. This template creates a DSL that resembles UML component diagrams.
  • Task Flow: use this template if your DSL includes workflows, states, or sequences. This template creates a DSL that resembles UML activity diagrams.
  • Minimal Language: use this template if your DSL does not resemble the other templates. This template creates a DSL that has two classes and one relationship.

Create a minimal language DSL and give it a name (MyDslSample). The wizard will register a new file type for models in your language. Therefore you should choose a file extension that is unique to your system. Accept the default icon for the model files, as it can be changed later if necessary. Accept the default values for the product name, company name, and top level namespace that will be used in the solution. Sign your assembly with a strong key name, review your choices and then click Finish. Visual Studio will create a solution consisting of two projects:

  • Dsl, defines the DSL and its editing and processing tools.
  • DslPackage, determines how the language tools integrate with Visual Studio.

After the solution is created, a large amount of code is generated automatically from the DSL description file (DslDefinition.dsl) by applying a custom tool called TextTemplatingGenerator to the files in T4 format.

Read More >>

Domain Specific Languages (I)

Domain Specific Languages are a relatively unknown piece of Visual Studio Extension technology. In this series of posts, I'm going to describe how to create a Domain Specific Language for modelling simple inheritance, and generating code from the model. The purpose is to do it from scratch, see the process, and understand the concepts involved. You’ll require Visual Studio 2008 Service Pack 1, and the Visual Studio SDK 1.1 to do this on your own machine.

Unlike a general purpose language such as C#, a domain-specific language is designed to handle a particular problem space, or domain. Domains can be defined in many different ways. Some domains are associated with specific industries or kinds of business (insurance domain, financial services domain, library domain). Other domains relate to specific kinds of software development (Web service components, GUI components etc.).

Typically, DSLs are created when a development team has to write similar code for different projects. For example, a team may have to develop several different insurance policy applications. The applications may use the same constructs, for example, a table that specifies the policy prices in different areas, in different ways. In a DSL, the price/area table can be a language element that is handled differently in each application.

DSLs can either be textual or graphical. An example of a textual DSL is an XML schema. Textual DSLs are difficult to develop and debug, and non-technical people may find them hard to understand. Graphical DSLs are easier to work on and easier to use for communicating with non-developers.

Domain-specific development is the process of identifying the parts of your applications that can be modelled by using a DSL, and then constructing the language and deploying it to application developers. The developers use the DSL to construct models that are specific to their applications, use the models to generate source code, and then use the source code to develop the applications.

The benefits of DSL development include:
  • DSL consists of elements and relationships that directly represent the logic of the problem space. Therefore, its easier to design the application, and find and correct errors of logic.
  • They let non-developers and people who do not know the domain understand the overall design.
  • Developers can use the code that their model generates to quickly create a prototype application that they can show to clients.
DSL Tools let you create a DSL that has your own graphical designer and your own diagram notation, and then use the language to generate appropriate source code for each project.
Read More >>

Wednesday, December 17, 2008

Visual Studio Project System

Following on from the previous post on Visual Studio extensibility (VSX), I'd like to point out an extremely useful resource for anyone looking at extending Visual Studio's project system. The IronPython project is hosted on CodePlex with all of it's source code for you to look at. From the perspective of Visual Studio, this shows you how to create a complete project system! So you can see implementations of new project and item templates with parameter substitution along with wizards. It also includes things like intellisense and a custom build environment for the IronPython language. A lot of this is achieved using another Codeplex project - the Managed Package Framework for Projects (MPFProj) - which gives you a framework for building a custom project environment in Visual Studio. At the moment MPFProj is not included as part of the Visual Studio SDK, and you need to include all the MPFProj source in your own project - but keep an eye on this project here (VSXtra) for a DLL containing a pre-compiled version of the MPFProj code.
Read More >>

Extending Visual Studio

I've recently had the opportunity to investigate some of the extensibility features in Visual Studio 2008, and not surprisingly there are a lot of different methods open to you. By extensibility I mean extending the functionality of Visual Studio over and above what's in the box in some way, not just customising the IDE to suit your way of working. Here are the main ways you can set about extending Visual Studio, starting with the simplest and moving to the most advanced.

Templates enable you to define your own variations on standard project types or items. So if you find yourself repeatedly recreating the same basic project over and over again, a new project template can save you a lot of effort. Simply create the basic project you'd like to reuse, and then use the "Export Template Wizard" from the File menu in Visual Studio. Then you'll have a new project type available whenever you create a new project in Visual Studio. You can do the same kind of thing with individual items, so for example you could have a new form type, already populated with some controls and code.

Macros are the next step up, and they work in a very similar way to macros in Office applications. Visual Studio has a macro recorder, so you can record a sequence of operations in the IDE for later play back. These macros are recorded as Visual Basic code, and Visual Studio has a built macro editing environment for tweaking the recorded code. Macros also work as a great learning tool, so if you want to learn how to automate something in Visual Studio you can record a macro and then analyse the code to see how it's done. For an overview of the range of functionality available check out this diagram of the Visual Studio Automation Object Model.

Add-ins come next and offer a couple of features over and above macros. For a start they can be distributed as compiled code and they can be written in any language that supports COM automation. Most importantly they provide a greater degree of integration with Visual Studio, allowing you to add commands to Visual Studio menus and toolbars, add new pages to Visual Studio's Options dialog, and create new tool windows in Visual Studio.

Top of the range though are Visual Studio Packages. To create a package you'll need to download and install the latest version of the Visual Studio SDK and accompanying documentation. The Visual Studio SDK and the package system enables you to do pretty much anything with Visual Studio. All of Visual Studio's existing functionality is written in the form of packages, so you can implement new editors, tool windows, project systems and even re-purpose the basic shell. The VSX website has loads of material on extending the Visual Studio IDE, and a catalogue of available extensions (some free, some commercial).
Read More >>