【问题标题】:System.InvalidOperationException in WCF and EFWCF 和 EF 中的 System.InvalidOperationException
【发布时间】:2014-10-29 01:04:07
【问题描述】:

我有一个包含 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 不是当前上下文模型的一部分。

【问题讨论】:

  • 我已经完成了尝试,相信我按照我在下面向您建议的方式实现它。对于 WCF Soap Services,泛型不是一个可行的选择。
  • 最佳实践是不要一直共享您的实体,而仅将它们保留在数据层,而是创建它们的 POCO 表示并传递 POCO。从你正在做的紧密耦合中抽象出来。它将为您省去很多麻烦和问题。

标签: c# entity-framework wcf


【解决方案1】:

使用基于泛型的服务合同不是一个好主意,您必须使用服务合同的所有可能数据类型更新您的 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);
}

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

【讨论】:

  • 它们应该包含它们的所有属性。
  • 不,我认为您不了解这是如何工作的。你是先做 DB First 的代码吗?我已经实现了这一点,并且效果很好。
  • 您的方法可以接受 BaseClass 类型的参数,但它们是 X、Y 或 Z 的实例……试试吧……通过线路传递 X 的实例,然后在服务器集上传递已添加状态
  • 还是错了,WCF接口和实现一定不能有泛型,去掉它们!!!
  • 对不起@T McKeown,你是对的。我改变了这一行: _context.Set().Add(entity);对于另一个:_context.Entry(entity).State = EntityState.Added;它有效!......我唯一的问题是当我尝试从一个特定的表中获取所有信息时。但没关系,非常感谢你们!
猜你喜欢
  • 2012-09-14
  • 1970-01-01
  • 2017-05-28
  • 2011-03-06
  • 1970-01-01
  • 2019-05-16
  • 1970-01-01
  • 2022-11-11
  • 2014-08-13
相关资源
最近更新 更多