System.InvalidOperationException in WCF and EF

本文关键字:and EF WCF in InvalidOperationException System | 更新日期: 2023-09-27 18:28:16

我有一个包含4个项目(XYZ.Domain、XYZ.Data、XYZ.Service和XYZ.Client)的解决方案(XYZ)。我需要运行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不是当前上下文的模型的一部分。

System.InvalidOperationException in WCF and EF

使用基于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         
    }

现在您需要添加BaseClass的所有KnownTypes,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);
}

正如我在上面已经解释过的,您可以通过使用上下文并设置实体状态来简单地附加实体。看看我做的服务类的实现。。。