简体   繁体   中英

System.InvalidOperationException in WCF and EF

I have a solution (XYZ) with 4 projects (XYZ.Domain, XYZ.Data, XYZ.Service and XYZ.Client). I need to run the WCF web service. 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

Additional information: The entity type BaseClass is not part of the model for the current context.

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.

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. The NetdataContractSerializer will do that, you should also have common assembly(s) that both the client and server share to hold these data types.

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.

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.

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

[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

[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. Look at the implementation of the Service class I did...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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