WCF和EF中的System.InvalidOperationException

时间:2014-10-29 01:04:08

标签: c# entity-framework wcf

我有一个包含4个项目的解决方案(XYZ)(XYZ.Domain,XYZ.Data,XYZ.Service和XYZ.Client)。我需要运行WCF Web服务。这是我的代码:

namespace XYZ.Domain
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.Text;
    using System.Threading.Tasks;

    // Updated properties
    [DataContract]
    public class X : BaseClass
    {
        [DataMember]
        [Key]
        public int Xx { get; set; }

        [DataMember]
        [Required]
        [StringLength(15)]
        public string Xy { get; set; }

        [DataMember]
        public bool Xz { get; set; }

        // Relationship with Y 1:1
        [DataMember]
        public virtual Y y { get; set; }
    }

    // Updated properties
    [DataContract]
    public class Y : BaseClass
    {
        [DataMember]
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int Yx {get; set;}

        [DataMember]
        [Required]
        [StringLength(15)]
        public string Yy {get; set;}

        [DataMember]
        public DateTime Yz {get; set;}

        // foreign key from Z
        [DataMember]
        public int Zx {get; set;}

        // Relationship with X 1:1
        [DataMember]
        public virtual X x {get; set;}

        // Relationship with Z *:1
        [DataMember]
        public virtual Z z { get; set; }
    }

    // Updated properties
    [DataContract]
    public class Z : BaseClass
    {
        public Z()
        {
            this.y = new HashSet<Y>();
        }

        [DataMember]
        [Key]
        public int Zx {get; set;}

        [DataMember]
        public DateTime Zy {get; set;}

        [DataMember]
        [Required]
        public float Zz {get; set;}

        // Relationship with Y 1:*
        [DataMember]
        public virtual ICollection<Y> y { get; set; }
    }

    // Added as a base class for my IServiceXYZ 
    [KnownType(typeof(X))]
    [KnownType(typeof(Y))]
    [KnownType(typeof(Z))]
    [DataContract]
    public class BaseClass
    {
        // empty
    }
}

这是数据层:

namespace XYZ.Data
{   
    using System;
    using System.Collections.Generic;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure;
    using System.Data.Entity.SqlServer;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using XYZ.Domain;

    public class Entities : DbContext
    {
        public Entities()
            : base("name=Database")
        {
            Database.SetInitializer<Entities>(new CreateDatabaseIfNotExists<Entities>());
        }

        // Set of XYZ.Domain entity class
        public DbSet<X> X { get; set; }
        public DbSet<Y> Y { get; set; }
        public DbSet<Z> Z { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<X>()
                .HasOptional(e => e.y)
                .WithRequired(e => e.x);

            modelBuilder.Entity<X>()
                .Property(e => e.Xy)
                .IsUnicode(false);

            modelBuilder.Entity<X>()
                .Property(e => e.Xz);

            modelBuilder.Entity<Y>()
                .Property(e => e.Yy)
                .IsUnicode(false);

            modelBuilder.Entity<Y>()
                .Property(e => e.Yz);

            modelBuilder.Entity<Z>()
                .HasMany(e => e.y)
                .WithRequired(e => e.z)
                .HasForeignKey(e => e.Zx)
                .WillCascadeOnDelete(false);

            modelBuilder.Entity<Z>()
                .Property(e => e.Zy);

            modelBuilder.Entity<Z>()
                .Property(e => e.Zz);
        }
    }

    // Interface for Generic Repository where T : XYZ.Domain Entities
    public interface IGenericRepository<T> where T : class, new()
    {
        void Insert(T entity);
        void Update(T entity);
        void Delete(T entity);
    }

    // Class generic for database persistence
    public class GenericRepository<T> : IGenericRepository<T> where T : class, new()
    {
        public void Insert(T entity)
        {
            using (var _context = new Entities())
            {
                try
                {
                    _context.Set<T>().Add(entity);
                    _context.SaveChanges();
                }
                catch(Exception ex)
                {
                    throw;
                }
            }
        }

        public void Update(T entity)
        {
            using (var _context = new Entities())
            {               
                try
                {
                    _context.Entry(entity).State = EntityState.Modified;
                    _context.SaveChanges();
                }
                catch(Exception ex)
                {
                    throw;
                }
            }
        }

        public void Delete(T entity)
        {
            using (var _context = new Entities())
            {                
                try
                {
                    _context.Set<T>().Remove(entity);
                    _context.SaveChanges();
                }
                catch(Exception ex)
                {
                    throw;
                }
            }
        }
    }   
}

这是服务层:

namespace XYZ.Service
{
    using XYZ.Domain;
    using XYZ.Data;

    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IServiceXYZ" in both code and config file together.
    [ServiceContract]
    public interface IServiceXYZ
    {
        [OperationContract]
        void Insert(BaseClass entity);

        [OperationContract]
        void Update(BaseClass entity);

        [OperationContract]
        void Delete(BaseClass entity);
    }

    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
    // NOTE: In order to launch WCF Test Client for testing this service, please select Service1.svc or Service1.svc.cs at the Solution Explorer and start debugging.
    public class ServiceXYZ : IServiceXYZ
    {
        private IGenericRepository<BaseClass> dao;

        public ServiceXYZ()
        {
            dao = new GenericRepository<BaseClass>();
        }

        public void Insert(BaseClass entity)
        {
            dao.Insert(entity);
        }

        public void Update(BaseClass entity)
        {
            dao.Update(entity);
        }

        public void Delete(BaseClass entity)
        {
            dao.Delete(entity);
        }
    }
}

这是客户端测试:

namespace XYZ.Test
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    // Service reference
    using XYZ.Test.ServiceXYZ;

    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                ServiceXYZOf_BaseClassClient client = new ServiceXYZOf_BaseClassClient();

                X x = new X
                {
                    Xx = 1,
                    Xy = "ABCDEFGHIJKLMNÑ",
                    Xz = false
                };

                client.Insert(x);
                client.Close();
            }
            catch (SystemException ex)
            {
                Console.Write(ex.Message);
            }

            Console.ReadLine();
        }
    }
}

但这是错误:

  

XYZ.Data.dll中出现“System.InvalidOperationException”类型的异常,但未在用户代码中处理

附加信息:实体类型BaseClass不是当前上下文模型的一部分。

1 个答案:

答案 0 :(得分:1)

使用基于Generics的服务合同不是一个好主意,您必须使用服务合同的所有可能数据类型更新web.config。

如果您坚持这种类型的配置,那么您需要让WCF服务使用将在有效负载中嵌入CLR数据类型信息的序列化程序。 NetdataContractSerializer将执行此操作,您还应该拥有客户端和服务器共享的公共程序集来保存这些数据类型。

如果你正在尝试做“纯粹的”WCF,那么你正在做的事情并不是要与你必须应对的所有问题进行讨论。

您可以创建一个发送任何对象的服务,该对象是某个基类的子类,例如,如果您的类X,Y和Z都扩展了基类(让我们称之为BaseClass)那么您的服务合同可以基于此。

所以你最终会得到这样的服务:

public class ServiceXYZ : IServiceXYZ
{
    // XYZ.Data persistence object



    public void Insert(BaseClass entity)
    {
        //get EF Context 
        var Context = new Entities();  // <-- some method to get your EF context
        Context.Entry(entity).State = Added;  //<- this will attach and add         
    }

现在你需要添加KnownTypes的所有BaseClass,WCF需要知道BaseClass可能的.NET类型是什么

[KnownType(typeof(X)]
[KnownType(typeof(Y)]
[KnownType(typeof(Z)]
[DataContract]
public class BaseClass
{
    ...
}

您需要更改数据合同以扩展BaseClass

[DataContract]   
public class X : BaseClass  { ...}

[DataContract]   
public class Y : BaseClass  { ...}

[DataContract]   
public class Z : BaseClass  { ...}

<强>更新

[ServiceContract]
public interface IServiceXYZ   // NO GENERICS!! YOU BASE CLASS
{
    [OperationContract]
    void Insert(BaseClass entity);

    [OperationContract]
    void Update(BaseClass entity);

    [OperationContract]
    void Delete(BaseClass entity);
}

正如我上面已经解释的那样,您可以通过使用Context并设置实体状态来简单地附加实体。看看我做过的Service类的实现......