简体   繁体   English

WCF和EF中的System.InvalidOperationException

[英]System.InvalidOperationException in WCF and EF

I have a solution (XYZ) with 4 projects (XYZ.Domain, XYZ.Data, XYZ.Service and XYZ.Client). 我有一个包含4个项目(XYZ.Domain,XYZ.Data,XYZ.Service和XYZ.Client)的解决方案(XYZ)。 I need to run the WCF web service. 我需要运行WCF Web服务。 This is my code: 这是我的代码:

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
    }
}

This is the Data layer: 这是数据层:

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;
                }
            }
        }
    }   
}

This is the Service layer: 这是服务层:

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);
        }
    }
}

And this is the client test: 这是客户端测试:

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();
        }
    }
}

But this is the error: 但这是错误:

An exception of type 'System.InvalidOperationException' occurred in XYZ.Data.dll but was not handled in user code XYZ.Data.dll中发生类型为'System.InvalidOperationException'的异常,但未在用户代码中处理

Additional information: The entity type BaseClass is not part of the model for the current context. 附加信息:实体类型BaseClass不是当前上下文模型的一部分。

Using a service contract based on Generics is not a good idea, you'd have to update your web.config with all the possible datatypes for your service contract. 使用基于Generics的服务合同不是一个好主意,您必须使用服务合同的所有可能数据类型更新web.config。

If you insist on this type of configuration then you need to have the WCF Service use a serializer that will embed CLR data type information in the payload. 如果您坚持这种类型的配置,那么您需要让WCF服务使用一个串行器,该串行器会将CLR数据类型信息嵌入有效负载中。 The NetdataContractSerializer will do that, you should also have common assembly(s) that both the client and server share to hold these data types. NetdataContractSerializer将执行此操作,您还应该具有客户端和服务器共享的通用程序集来保存这些数据类型。

If you are trying to do "pure" WCF then what you are doing is not going to jive with all the gotcha's that you'll have to contend with. 如果您要尝试“纯粹”的WCF,那么您要做的事情就不会与您必须应对的所有陷阱混为一谈。

You can create a service that sends any object that is a subclass of some base class, for example if your class X, Y and Z all extended a base class ( let's call it BaseClass ) then your service contract could be based off of that. 您可以创建一个发送任何对象的服务,该对象是某个基类的子类,例如,如果您的X,Y和Z类都扩展了一个基类(我们称之为BaseClass ),则您的服务合同可以以此为基础。

So you'd end up with a service like this: 因此,您最终将获得如下服务:

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         
    }

Now you need to add all KnownTypes of BaseClass , WCF needs to know what the possible .NET types could be for BaseClass 现在,您需要添加所有KnownTypesBaseClass ,WCF需要知道可能的.NET类型可能是什么BaseClass

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

And you'd need to change your data contract to extend the BaseClass 而且您需要更改数据协定以扩展BaseClass

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

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

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

UPDATE 更新

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

    [OperationContract]
    void Update(BaseClass entity);

    [OperationContract]
    void Delete(BaseClass entity);
}

As I have already explained above you can simply attach an entity by using the Context and setting an entities state. 如前所述,您可以使用Context并设置一个实体状态来附加一个实体。 Look at the implementation of the Service class I did... 看一下我所做的Service类的实现...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM