Books Authors Web Service Implementation (EDM Sample Application)

The Books Authors Web Service example discussed in this group of topics implements an Entity Data Model (EDM) application from the schemas in the topic named Books Authors Web Service Schemas (EDM Sample Application).

The namespace and classes used by the Books Authors Web Service are built from the entities and associations designed in conceptual schema definition language (CSDL). Database tables that store data for the classes are described by metadata written in store schema definition language (SSDL). The types in the design schema are mapped to store metadata in the mapping specification language (MSL).

The programmable object model depends on both the schemas and mapping specification. The object model is built from the conceptual schema; the resulting DLL requires the conceptual and storage schemas and the mapping specification to be in scope for an entity connection to be established.

Establishing the EntityConnection makes the classes and data source available to code. For more information about building the class library, see Implementing Entities (EDM).

Web Service Project

The Web service that uses the Books Authors object model described in this section is implemented as a separate project. Create a project that is based on the ASP.NET Web service template. Add references to the System.Data.Entity DLL and to the BooksAuthors DLL created in the EDM Model Object project. Adding the references creates a bin folder that contains the DLLs.

Schemas must be in scope of the executable. Add the schemas from the BooksAuthors class library project to the App_Data folder in the Web service project.

Web.config

A Web.config file is necessary to locate schemas, metadata, and the data server that is used by EDM objects in the BooksAuthors Web Service. The following XML shows the Web.config file for the service. The connection string identifies the server and database used by the mapping specification to establish a connection between the programmable classes and the database.

<?xml version="1.0"?>
<configuration>

    <configSections/>

    <system.web>
        <authentication mode="Windows"/>
        <identity impersonate ="true"/>
    </system.web>

    <connectionStrings>
        <add name="BooksAuthorsEntities"
         connectionString=
         "metadata=C:\Inetpub\wwwroot\BooksAuthorsWebSrv\App_Data\;
         provider=System.Data.SqlClient; 
         provider connection string='server=serverName; 
         database=BooksAuthors; 
         integrated security=true;
         multipleactiveresultsets=true'" 
         providerName="System.Data.EntityClient"/>
    </connectionStrings>
</configuration>

Note

This connection string sets multiple active result sets to true as required to invoke the Load method on associations when another data reader is already open on the same connection.

When the service is running on a Web server it must find the path to the schemas from a virtual directory. One way to make this work is to use the Server.MapPath method in the global.asax file. In the following code, the MapPath method finds the fully qualified absolute path. The path found by MapPath is substituted in the connection string obtained from the Web.config file.

The procedures are demonstrated after the Application_Start method in the global.asax file shown immediately below.

  <script runat="server">

    void Application_Start(object sender, EventArgs e) 
    {
        String connString = 
          System.Web.Configuration.WebConfigurationManager.
          ConnectionStrings["BooksAuthorsEntities"].ConnectionString;

        connString = connString.Replace(@"C:\Inetpub\wwwroot\BooksAuthorsWebSrv\App_Data\",
         Server.MapPath("~/App_Data"));

        Application.Contents.Add("ConnString", connString);

    }

  </script>

In the Web Method declarations, the altered connection string is read from the Application.Contents collection and used to instantiate the object context as follows: BooksAuthorsEntities db = new BooksAuthorsEntities(Application.Contents["ConnString"] as String).

Application Code

Establishing the entity connection depends on the connection string in the Web.config file and code in the Web service constructor. The connection string is stored in the Web.Config file, therefore, the connection can be established in one line of code: BooksAuthors db = new BooksAuthors(Application.Contents["ConnString"] as String)). This Web service initializes the entity connection separately in each method provided by the service. SQL Server uses connection pooling so this does not impede performance.

The Web service methods in this application are implemented as public methods that initialize and return List<T>. The lists are converted to text by the protocols of Web services and returned as XML data.

The first method returns all the Books entities in the system. In order to retrieve the Books data, a connection to the BooksAuthors data is opened to the Web service. A new List<T> of type Books is initialized and used to convert the Books Query<T> to an array that can be returned to the caller of the method. Then the connection is closed.

    [WebMethod]
    public Books[] GetBooks()
    {
        using (BooksAuthorsEntities db =
            new BooksAuthorsEntities(
                Application.Contents["ConnString"] as String))
        {
            List<Books> bookList = new List<Books>(db.Books);
            return bookList.ToArray();
        }
    }

The next method works the same way to return Authors.

    [WebMethod]
    public Authors[] GetAuthors()
    {
        using (BooksAuthorsEntities db =
            new BooksAuthorsEntities(
                Application.Contents["ConnString"] as String)) 
        {
            List<Authors> authorsList = new List<Authors>(db.Authors);
            return authorsList.ToArray();
        }
    }

The following method uses a parameterized query on the book title to find all the BooksInfo entities where the BookTitle property is equal to the supplied title. By using these BooksInfo entities, the association between Authors entities and the BooksInfo entities can be navigated to access all Authors entities associated with the book of the given title.

An array of type Authors is returned to the caller of the method.

    [WebMethod]
    public Authors[] GetAuthorsFromBookTitle(string bookTitle)
    {
        using (BooksAuthorsEntities db =
            new BooksAuthorsEntities(
                Application.Contents["ConnString"] as String))
        {
            ObjectParameter param = new ObjectParameter("p", bookTitle);

            List<Authors> authorsList = new List<Authors>();

            foreach (BooksInfo bksInfo in db.BooksInfo.Where(
                "it.BookTitle = @p", param))
            {
                bksInfo.AuthorsReference.Load();
                authorsList.Add(bksInfo.Authors);

            }
            return authorsList.ToArray();
        }
    }

The GetBooksFromAuthorLastName method works like the previous method to return an array of books based on a query on an author's last name.

    [WebMethod]
    public Books[] GetBooksFromAuthorLastName(string authorLastName)
    {
        using (BooksAuthorsEntities db =
            new BooksAuthorsEntities(
                Application.Contents["ConnString"] as String))
        {
            ObjectParameter param = new ObjectParameter("p", authorLastName);

            List<Books> booksList = new List<Books>();

            foreach (BooksInfo bksInfo in db.BooksInfo.Where(
                "it.AuthorLastName = @p", param))
            {
                bksInfo.BooksReference.Load();
                booksList.Add(bksInfo.Books);
            }
            return booksList.ToArray();
        }
    }

This Web service can also be used to add Books entities and the author or authors of the book being added. The entity connection can be used to write data and to read it. The following Web method creates entities that represent a book and its author, adds the entities to the ObjectContext, and updates the database. The method can be used as many times as necessary to add more authors for the same title.

    [WebMethod]
    public void AddBook(string title, string authorFirstName,
        string authorLastName, string infoUri, string isbnNumber)
    {
        using (BooksAuthorsEntities db =
            new BooksAuthorsEntities(
                Application.Contents["ConnString"] as String))
        {

            BooksInfo newBooksInfo = new BooksInfo();
            newBooksInfo.BookInfoId = Guid.NewGuid();
            newBooksInfo.AuthorLastName = authorLastName;
            newBooksInfo.BookTitle = title;
            if (!infoUri.Equals(""))
                newBooksInfo.InfoLocator = infoUri;

            Books existingBook = null;
            ObjectParameter param = new ObjectParameter("p", title);
            ObjectQuery<Books> queryBook = db.Books.Where(
                "it.Title = @p", param);

            if (queryBook.Exists())
            {
                existingBook = db.Books.Where(
                    "it.Title = @p", param).First();
                newBooksInfo.Books = existingBook;
            }
            else
            {
                Books newBook = new Books();
                newBook.BookId = isbnNumber;
                newBook.Title = title;
                newBooksInfo.Books = newBook;
                db.AddToBooks(newBook);
            }

            Authors existingAuthor = null;
            ObjectParameter aParam = new ObjectParameter(
                "p", authorLastName);
            ObjectParameter aParam2 = new ObjectParameter(
                "q", authorFirstName);
            ObjectParameter[] pars =
                new ObjectParameter[] { aParam, aParam2 };
            ObjectQuery<Authors> queryAuthor = db.Authors.Where(
                "it.LastName = @p AND it.FirstName = @q", pars);

            if (queryAuthor.Exists())
            {
                existingAuthor = db.Authors.Where(
                    "it.LastName = @p AND it.FirstName = @q",
                    pars).First();
                newBooksInfo.Authors = existingAuthor;
            }
            else
            {
                Authors newAuthor = new Authors();
                newAuthor.AuthorId = Guid.NewGuid();
                newAuthor.LastName = authorLastName;
                newAuthor.FirstName = authorFirstName;
                newBooksInfo.Authors = newAuthor;
                db.AddToAuthors(newAuthor);
            }

            db.AddToBooksInfo(newBooksInfo);
            db.SaveChanges();

        }
    }

The last method in this example returns BooksInfo entities that contain book title, book ID number, author last name, and locator information.

    [WebMethod]
    public BooksInfo[] GetBooksInfo()
    {
        using (BooksAuthorsEntities db =
            new BooksAuthorsEntities(
                Application.Contents["ConnString"] as String))
        {
            List<BooksInfo> booksInfoList =
                new List<BooksInfo>(db.BooksInfo);

            return booksInfoList.ToArray();
        }
    }

This method is used to display data in the client applications shown in Client Application for Web Service (EDM Sample Application).

See Also

Concepts

Books Authors Web Service (EDM Sample Application)
Books Authors Web Service Schemas (EDM Sample Application)
Client Application for Web Service (EDM Sample Application)

Other Resources

EDM Specifications
Schemas and Mapping Specification (Entity Framework)