[转]Using The Entity Framework With WCF

本文介绍了一个解决方案,使用POCO对象与Entity Framework结合实现WCF服务的无状态设计。通过创建实体模型、定义上下文类、实体类以及仓库类,实现了产品和销售的CRUD操作。此外,文章提供了详细的步骤和代码示例,帮助开发者在WCF服务中高效地使用Entity Framework。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文转自:http://www.gavindraper.co.uk/2010/12/07/using-the-entity-framework-with-wcf/

I’ve had a few problems when I first started trying to use the entity framework with WCF mainly because it needed to be stateless between requests and all the examples/tutorials I’ve seen involve the context being held on to for the life of an entity to manage changes.

I eventually got to a solution that I’m fairly happy with and am going to run through a simple step by step version of it. In this solution I’m going to be using POCO objects with the entity framework for this to work you need to download and install the EF4 CTP from Microsoft .

You will also need SQL Server and .Net 4.

First create a new database with the following 2 tables.

In this example we have a products table and a sales table, it’s a one too many relationship between products and sales.

The next step is to create our Entity Model.

In visual studio

  • Create a new solution
  • Add a class library project to the solution called WCFEntityData
  • Add a WCF service application to the solution called WCFEntityService
  • Add a console app to the solution called WCFEntityConsoleTest

In the WCFEntityData project…

  • Add a reference to the EF4CTP normally found in C:\Program Filess\Microsoft ADO.NET Entity Framework Feature CTP4\Binaries
  • Delete Class1.cs
  • Add a folder called Repositories
  • Add a folder called Model
  • Add a folder called Entities

The Model

Add a new “ADO.Net Entity Data Model” called WcfEntityModel to the Model folder.

Point it at your database and give the connection settings a name of “DbEntitiesâ€

Click the tables node to model all the tables and give the model a namespace a name of WcfEntityModel.

You should then have a model that looks like this

You then need to delete the Product navigation property from the Sale entity as it creates a circular reference which causes problems with serialization.

In the properties for the model set the ‘Code Generation Stratergy’ to none, this stops the entity framework generating your entities classes for you as in this case we are going to build our own POCO objects that are perfect for serialization in WCF.

For each of the objects in your model click the Version field, then in their properties window set the ‘Concurrency Mode’ to fixed. This means when we try to update an object to the database if the version field in the database is different to the one in the object then someone has edited the object after we retrieved it, this will cause EF to throw an exception to avoid concurrency issues. The timestamp data type in MS SQL will automatically get updated every time a record is added or edited so you don’t need to worry about maintaining this field.

Add a new class to the Model directory called ‘WcfEntityContext’. Make this class look like

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
using System.Collections.Generic;
using System.Data;
using System.Data.Objects;
using WCFEntityData.Entities;
 
namespace WCFEntityData.Model
{
    public class WcfEntityContext : ObjectContext
    {
        public ObjectSet<Product> Products { get ; set ; }
        public ObjectSet<Sale> Sales { get ; set ; }
 
        public WcfEntityContext() : this ( "name=DbEntities" ) { }
 
        public WcfEntityContext( string connectionString)
            : base (connectionString, "DbEntities" )
        {
            Products = CreateObjectSet<Product>();
            Sales = CreateObjectSet<Sale>();
            //Turned off to avoid problems with serialization
            ContextOptions.ProxyCreationEnabled = false ;
            ContextOptions.LazyLoadingEnabled = false ;
        }
 
        public void SetModified( object entity)
        {
            ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
        }
 
        public void AttachModify( string entitySetName, object entity)
        {
            AttachTo(entitySetName, entity);
            SetModified(entity);
        }
 
        public void AttachModify( string entitySetName, IEnumerable<Object> entities)
        {
            if (entities != null )
                foreach (var entity in entities)
                    AttachModify(entitySetName, entity);
        }
 
    }
}

This class is telling the Entity Framework how to map your Entity Model to your POCO objects. If Proxy Creation is true the entity framework wraps your POCO objects in proxy classes for change tracking, as we are working disconnected there will be no change tracking so I disabled this in the constructor where I also disabled Lazy Loading as again due to the disconnected state Lazy Loading will not work over WCF. The 3 methods at the end are just helper methods that we will make use of later.

The Entities

Now its time to create our entities. In the Entities folder create two classes called ‘Product’ and ‘Sale’

The product class should look like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System.Collections.Generic;
using System.Runtime.Serialization;
 
namespace WCFEntityData.Entities
{
    [DataContract]
    public class Product
    {
        [DataMember] public int Id { get ; set ; }
        [DataMember] public string Name { get ; set ; }
        [DataMember] public decimal Price { get ; set ; }
        [DataMember] public byte [] Version { get ; set ; }
        [DataMember] public virtual IList<Sale> Sales { get ; set ; }
    }
}

The sale class should look like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System;
using System.Runtime.Serialization;
 
namespace WCFEntityData.Entities
{
    [DataContract]
    public class Sale
    {
        [DataMember] public int Id { get ; set ; }
        [DataMember] public int ProductId { get ; set ; }
        [DataMember] public decimal Price { get ; set ; }
        [DataMember] public DateTime SaleDate { get ; set ; }
        [DataMember] public byte [] Version { get ; set ; }
    }
}

The attributes of DataContract and DataMember are needed for the Entity Framework to use these objects. Notice on the product object I have a virtual list of sale objects, making this virtual allows for entity framework to use lazy loading. In this case I’m going to be turning lazy loading off as it doesn’t really work for the example over WCF as the objects will be disconnected once they hit the client so no lazy loading can occur. The reason I still made the property virtual is so this could possible use Lazy Loading in the future.

The Repository

Lets start out simple create a new class in the Repositories folder called ProductRepository.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
using System.Collections.Generic;
using System.Linq;
using WCFEntityData.Entities;
using WCFEntityData.Model;
 
namespace WCFEntityData.Repositories
{
    public class ProductRepository
    {
        public static Product New(Product product)
        {
            using (var ctx = new WcfEntityContext())
            {
                ctx.Products.AddObject(product);
                ctx.SaveChanges();
                return product;
            }
        }
 
        public static Product Update(Product product)
        {
            using (var ctx = new WcfEntityContext())
            {
                ctx.AttachModify( "Products" , product);
                ctx.AttachModify( "Sales" , product.Sales);
                ctx.SaveChanges();
                return product;
            }
        }
 
        public static IList<Product> GetAll()
        {
            using (var ctx = new WcfEntityContext())
            {
                var sales = (from s in ctx.Products.Include( "Sales" ) select s).ToList();
                return sales;
            }
        }
    }
}

This gives us a method to create a new product with any sales you want to add to it and a method to return all the products and their associated sales. Notice how the WcfEntityContext is wrapped in a using this is because WCF is stateless so we are having to create and destroy the context as and when we need it.

In the WcfEntityService Project

Now we need to define the methods our service is going to expose.

Add a reference to your WcfEntityData project.

Open app.config in your WcfEntityData project and copy everything between and including <connectionStrings> and </connectionStrings>. Open web.config in the WcfEntityService project and paste the connection string info right below the opening tag.

Open the IService1.cs file and change it to look like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System.Collections.Generic;
using System.ServiceModel;
using WCFEntityData.Entities;
 
namespace WcfEntityService
{
    [ServiceContract]
    public interface IService1
    {
        [OperationContract] IList<Product> ProductGetAll();
        [OperationContract] Product ProductUpdate(Product product);
        [OperationContract] Product ProductNew(Product product);
    }
}

Open the Service1.svc file and change it to look like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System.Collections.Generic;
using WCFEntityData.Entities;
using WCFEntityData.Repositories;
 
namespace WcfEntityService
{
    public class Service1 : IService1
    {
        public Product ProductNew(Product product)
        {
            return ProductRepository.New(product);
        }
 
        public Product ProductUpdate(Product product)
        {
            return ProductRepository.Update(product);
        }
 
        public IList<Product> ProductGetAll()
        {
            return ProductRepository.GetAll();
        }
    }
}

We have now defined three methods on our service that wrap the three methods in our repository for fetching, inserting and updating products. Right click the service project and click build so we can then reference it in our other projects.

In the WcfEntityConsoleTest Project

Right click the project and click ‘Add Service Reference’. As the service is in the solution just click the discover button and it will find our service, give it a namespace of “MyService” and click OK.

Now your console app knows about the service it has access to all of its methods.

Open Program.cs and make it look like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
using System;
using WcfEntityConsoleTest.MyService;
 
namespace WcfEntityConsoleTest
{
    class Program
    {
        static void Main( string [] args)
        {
            CreateProduct();
            GetAllProducts();
            UpdateProduct();
            Console.ReadLine();
        }
 
        private static void CreateProduct()
        {
            var newProduct = new Product()
             {
                 Name = "Skates" ,
                 Price = ( decimal )250.99,
                 Sales = new Sale[]
                     {
                         new Sale(){SaleDate = DateTime.Now, Price = ( decimal )240},
                         new Sale(){SaleDate = DateTime.Now, Price = ( decimal )230},
                        new Sale(){SaleDate = DateTime.Now, Price = ( decimal )235},
                    }
             };
            var client = new Service1Client();
            var savedTransition = client.ProductNew(newProduct);
            Console.WriteLine( "Inserted Product ID Is : " + savedTransition.Id.ToString());
            Console.WriteLine( "--------------" );
        }
 
        private static void UpdateProduct()
        {
            var client = new Service1Client();
            var products = client.ProductGetAll();
            products[0].Name = "Updated Product" ;
            client.ProductUpdate(products[0]);
        }
 
        private static void GetAllProducts()
        {
            var client = new Service1Client();
            var products = client.ProductGetAll();
            foreach (var p in products)
            {
                Console.WriteLine(p.Name + " : " + p.Price);
                if (p.Sales != null )
                    foreach (var s in p.Sales)
                    {
                        Console.WriteLine( "   " + s.Id.ToString());
                        Console.WriteLine( "   " + s.Price);
                        Console.WriteLine( "   " + s.SaleDate.ToShortDateString());
                    }
            }
        }
    }
}

If you now run the console app, it should insert a new Product with 2 associated sales and tell you the ID of the newly inserted product. It should then list all the products in the database and their sales and then update the product with a new name. Try running it a couple of times to see the product collection grow.

Where Next

I know that’s a very basic example but it should be a good foundation to working with the Entity Framework in a disconnected way over WCF.

Obviously you would also want to create a Sales repository for manipulating sales. You will probably need a GetById method and a GetByName method. The example is also missing delete methods. All of this should be very simple to implement by just adding the required CRUD functionality to the Repository and then wrapping it in the service.

This example is also missing any error handling. You will want to implement some sort of Exception handling on the service so the client can get a helpful exception when something goes wrong rather than the generic WCF exception. This can be achieved by using FaultException, information on FaulExceptions can be found here.

转载于:https://www.cnblogs.com/freeliver54/archive/2013/03/22/2975249.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值