Using Domain Service with ASP.NET Dynamic Data
Last week .NET RIA services was released. In the release the Asp.NET team has released the Domain Data source control and Dynamic Data support for RIA services.Both the control and Dynamic Data support are the part of the toolkit release of RIA services.
In this post I am going to cover the steps on how to get started with a functional Dynamic Data application with Domain Service. The names RIA services and Domain service refer to the same service class.
Where to get it and install it?
The Domain Data Source control and Dynamic Data support is shipped as part of the toolkit release of .NET RIA Services toolkit. You would need to install the following to use Dynamic Data.
Visual Studio 2010
Silverlight 4
Steps on getting a functional Dynamic Data app working with Domain Service
1. Creating a Dynamic Data application
After you install the toolkit you would see the “ASP.NET Dynamic Data Domain Service Web Application” Project templates in Visual Studio. There are both C# and VB versions of the template.
2. Add a LinqToSql or Entity Framework data Model
For this sample I added an Entity Framework model against Northwind database and I added the Products table.
Since Domain Service can work with either a LinqToSql or Entity Framework model and the ASP.NET Domain Data source control talks to the Domain Service class, there is only one Dynamic Data project template that works for both Linq and Entity Framework models.Unlike earlier releases Dynamic Data had different templates for Linq and Entity Framework since they had different data sources(Linq and Entity data source) to talk to different data models.
3. Compile the project
Make sure that you compile the project. The reason you want to do this is because in the next step when we create the Domain Service class, the Domain Service class reflects over the data model to figure out information about the tables in the model. Without compiling the project the Domain service would not be able to reflect over the model.
4. Add a Domain Service class
You can add the Domain Service by “Adding a New Item” to the project. “Domain Service class” can be found in the “Web” Node of “Add New Item” dialog.
You can change the default name of the Domain Service to what you want. For this post I am naming it Northwind
You can select the Entities in the “Add New Domain Service” dialog that would come up after you add Domain Service. Following are the suggestions on how to configure this dialog and it should look like the one in the image below
1. Uncheck “Enable Client access” and “Expose OData endpoint”, since they are only used when you are building a RIA services application for a Silverlight client
2. Check “Enable Editing”,it will generate Insert, Update and Delete methods in the Domain Service class for the entity
3. Check “Generate associated class for metadata”, it generates partial classes for the entit(y/ies) selected.You can define metadata attributes on the entity/columns of the entity
4. Available DataContext/ObjectContext classed: This drop down shows the data model classes that you have in your project. Since for this sample you must have added only one the drop down shows that class.
Your Domain Service class should look something like this
public class Northwind : LinqToEntitiesDomainService<NorthwindEntities>
{
// TODO:
// Consider constraining the results of your query method. If you need additional input you can
// add parameters to this method or create additional query methods with different names.
// To support paging you will need to add ordering to the 'Products' query.
public IQueryable<Product> GetProducts()
{
return this.ObjectContext.Products;
}
public void InsertProduct(Product product)
{
if ((product.EntityState != EntityState.Detached))
{
this.ObjectContext.ObjectStateManager.ChangeObjectState(product, EntityState.Added);
}
else
{
this.ObjectContext.Products.AddObject(product);
}
}
public void UpdateProduct(Product currentProduct)
{
this.ObjectContext.Products.AttachAsModified(currentProduct, this.ChangeSet.GetOriginal(currentProduct));
}
public void DeleteProduct(Product product)
{
if ((product.EntityState == EntityState.Detached))
{
this.ObjectContext.Products.Attach(product);
}
this.ObjectContext.Products.DeleteObject(product);
}
}
If you are using Entity Framework as the data model then you would have to update your query method for every entity. You need to add the ordering clause. The reason that you need to do this is because by default the query method is not ordered and in Dynamic Data project templates, paging is enabled for the List and List details templates. In the case if you do not order the result of your query method and use paging you will get the following exception when you access the entity in List/List details template
The method 'Skip' is only supported for sorted input in LINQ to Entities. The method 'OrderBy' must be called before the method 'Skip'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.NotSupportedException: The method 'Skip' is only supported for sorted input in LINQ to Entities. The method 'OrderBy' must be called before the method 'Skip'.
You can order the result of GetProducts() method shown in code above as follows. This would return the list of products ordered by ProductID
public IQueryable<Product> GetProducts()
{
return this.ObjectContext.Products.OrderBy(p=>p.ProductID);
}
5. Registering Domain Service with Dynamic Data
Open Global.asax.cs and uncomment the following line. Replace YourDomainServiceType with the name of Domain Service that you created eg. “Northwind” and set ScaffoldAllTables=true
DefaultModel.RegisterContext(new DomainModelProvider(typeof(Northwind)), new
ContextConfiguration() { ScaffoldAllTables = true });
6. Run the app
You should now be able to build your app and run it. When you run it Default.aspx page would be launched and will show you all the entity types that have a Query Method defined.
Comments
Anonymous
August 26, 2010
Update (8/26/2010): I updated some of the content to reflect the new way to get the bits. OneAnonymous
October 06, 2010
So I wanted to implement a "bulk" insert in my domain service. I am using Dynamic Data and ASP.NET...all the domain service examples I have seen refer to using Client generated Object Context class in SIlverlight examples...I'm guess this isn't applying to my case since all the processing I am doing with DD is happening server side....so would i just add my custom bulk methods to my Domain Service class and add the <ignore>_ attribute...is this save? Also would I explicistly have to call SaveChanges() ? The docs for Silverlight are good but as this applies to Dynamic Data and ASP.NET I have to save only very basic examples are covered....Anonymous
October 06, 2010
@Don: You are correct in your understanding. The Domain service scenario works differently in Aspnet application vs Silverlight. In AspNet there is no Client generated so you are working with the Domain Service directly. And when you use Databound control with DomainDatSource, you are limited to a single row update by default. That being said you can have a CustomUpdate method fired in the DomainService which could update more than 1 row of the entity provided that you handle the SaveChanges(). Following linek shows you how to use SaveChanges msdn.microsoft.com/.../bb336792.aspx Regarding documentation we have something in works which would add to the existing ones and I would like to hear from you on what is missing currently.Anonymous
October 07, 2010
Well basically how to consume the DomainService programmitcally in an ASP.NET page other than just using the DomainDataSource control. An example and an explanation would go a long way. Do you have any samples I could see? I am just trying to get past this one last hurdle...also do you need to do anything special to instantiate the DomainService in ASP.NET code behind? I'm not sure whether I am instantiating the service correctly....Anonymous
October 10, 2010
Unfortunately I do not have CodeSample handy at this point and would have to build one. You can do Custom buk operations within the domain service as well eg you can put this in Update for Northwind Products which updates all product rows based on a condition public void UpdateProduct(Product currentProduct) { IQueryable<Product> result= this.ObjectContext.Products.Where(p=>p.UnitsInStock<10); try { foreach (Product item in result) { item.Discontinued = true; } try { // Try to save changes, which may cause a conflict. int num = this.ObjectContext.SaveChanges(); } catch (OptimisticConcurrencyException) { // Resolve the concurrency conflict by refreshing the // object context before re-saving changes. this.ObjectContext.Refresh(RefreshMode.ClientWins, result); // Save changes. this.ObjectContext.SaveChanges(); } } catch (UpdateException) { } } You can also refer to the following sample which shows how you can do edits on multiple rows using gridview www.codeproject.com/.../BulkEditGridView.aspx You would have to extend this sample to work with Domain Service In general I would say that you should use Domain Data source server control if you want to use the Databound controls, but if you do not want to use the control then you could do something as follows. Following post shows how you can use DomainService with MVC blog.bmdiaz.com/.../using-ria-domainservices-with-asp.net-and-mvc-2.aspx You can follow the same pattern for creating a domainservice and then bind it to the Gridview without using the DataSourceAnonymous
October 23, 2010
The comment has been removedAnonymous
October 23, 2010
The reason you get this exception is that you have multiple Query methods and Dynamic Data need to know which one to use so you have to make one as the default query method. eg [Query(IsDefault =true)] GetProductsCustomSorting() More info about the attribute msdn.microsoft.com/.../system.servicemodel.domainservices.server.queryattribute(v=VS.91).aspxAnonymous
October 25, 2010
Thank you very much. I got it. Another problem: Following your blog, I just created an ASP.NET Dynamic Data Domain Service Web application without adding any other code. There is no any problem doing “Edit”, “Update”. Then I create the same application, but using table “Employee” of “AdventureWorks” database. After display “Employees”, I click “Edit”, modified a “Title” column. But I got following error after click “Update”. Cannot insert duplicate key row in object 'HumanResources.Employee' with unique index 'AK_Employee_rowguid' But no such error in a Dynamic Data Entity Web application. What’s wrong? I didn’t add any code.