ADO.NET Entity Framework on Vs2012 + WCF = Error - wcf

First of all, thanks for reading my question.
I am developing a solution using VS 2012 using ADO.NET Entity Framework (5 i think, the latest version). All is working fine until I introduce a WCF service as a Business Layer (this is an assignment for school, I cannot scrap WCF from Business Layer).
The issue is that when I request data from the database. When I have a method that returns a string from the database, it works just fine (since its a primitive). But when it returns an Entity object (Such as Account), it all goes to hell.
Exception: (Yeah, its very vague).
An error occurred while receiving the HTTP response to http://localhost:8733/Services/AccountsManager. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details.
What I tried: I tried modifying the Entites.tt file to add the [DataContract] [DataMember] Attribute. This is because in the older versions of EF, it seemed to be doing it on its own. But I do not know if this is neccessary since it allows me to compile and does not complain that it is not serializable.
This is how it looks at first:
namespace CommonLayer
{
using System;
using System.Collections.Generic;
public partial class Account
{
public Account()
{
this.Transactions = new HashSet<Transaction>();
this.Transactions1 = new HashSet<Transaction>();
}
public System.Guid ID { get; set; }
public int Type { get; set; }
public string Name { get; set; }
public int Currency { get; set; }
public decimal Balance { get; set; }
public System.DateTime DateOpened { get; set; }
public Nullable<int> Duration { get; set; }
public string UserName { get; set; }
public virtual AccountType AccountType { get; set; }
public virtual Currency Currency1 { get; set; }
public virtual User User { get; set; }
public virtual ICollection<Transaction> Transactions { get; set; }
public virtual ICollection<Transaction> Transactions1 { get; set; }
}
}
How it looks after modification:
namespace CommonLayer
{
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
[DataContract] public partial class Account
{
public Account()
{
this.Transactions = new HashSet<Transaction>();
this.Transactions1 = new HashSet<Transaction>();
}
[DataMember] public System.Guid ID { get; set; }
[DataMember] public int Type { get; set; }
[DataMember] public string Name { get; set; }
[DataMember] public int Currency { get; set; }
[DataMember] public decimal Balance { get; set; }
[DataMember] public System.DateTime DateOpened { get; set; }
[DataMember] public Nullable<int> Duration { get; set; }
[DataMember] public string UserName { get; set; }
public virtual AccountType AccountType { get; set; }
public virtual Currency Currency1 { get; set; }
public virtual User User { get; set; }
public virtual ICollection<Transaction> Transactions { get; set; }
public virtual ICollection<Transaction> Transactions1 { get; set; }
}
}
Any pointers are greatly appreciated.
My WCF Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
namespace BusinessLayer
{
[ServiceContract]
interface IAccountsManager
{
[OperationContract]
List<CommonLayer.Account> GetAccounts(String UserName);
[OperationContract]
String GetData();
[OperationContract]
CommonLayer.Account GetAccount(String UserName);
}
class AccountsManager: IAccountsManager, IDisposable
{
public List<CommonLayer.Account> GetAccounts(String UserName)
{
return DataLayer.AccountsRepository.Instance.GetAccountList(UserName).ToList();
}
public String GetData()
{
CommonLayer.Account acc = this.GetAccounts("test").FirstOrDefault();
return acc.DateOpened.ToString();
}
public CommonLayer.Account GetAccount(String UserName)
{
return this.GetAccounts(UserName).FirstOrDefault();
}
public void Dispose()
{
DataLayer.AccountsRepository.Reset();
}
}
}

You need to use a DTO (Data Transfer Object) and map from your EF object to your DTO.
So the service might accept an object called MyDto looking like:
[DataContract]
public class MyDto {
[DataMember]
public int Id {get;set;}
}
and a static mapping class with methods
public static MyEntity Map(MyDto dto) {
return new MyEntity { Id = dto.Id };
}
public static MyDto Map(MyEntity entity) {
return new MyDto { Id = entity.Id };
}
You can then map as required so that the service can use the DTO and Entity Framework can use the Entity.

It seems that when the EF class has Navigation properties (like ICollection<Transaction> Transactions in your class) it will fails when being transferred over WCF.
After many searches I could not find any solution except mapping an EF class into equivalent DTO class that is exactly the same as the EF class except the Navigation propertis and the constructor (i.e. I have stripped all virtual propertis (like ICollection ones) and the ctor from the EF class and have created from that a new class called the same as my EF class plus Dto suffix (e.g. CustomerDto).
I've used AutoMapper to automatically map an EF object into Dto-equivalent one. E.g:
var customer = getCustomer(cid);
var customerDto = Mapper.Map<CustomerDto>(customer);
return customerDto;
My WCF contract includes
[OperationContract]
CustomerDto getCustomerData(int cid);
In addition I needed to do one time initialization of the Mapper. I've done it within Global.asax as follows:
Mapper.CreateMap<Customer, CustomerDto>();

What I have done is changed my Entities.Edmx file. Firstly, I deleted all (2) ".tt" files. Then i changed Code Generation Strategy from None to Default. This seemed to solve all my problems.

Related

WCF with abstract base class with implemented interfaces does not serialize properly?

On the service side I have an abstract base class like so:
[DataContract]
public abstract class EntityBase : IObjectState, IDatabaseMetaData
{
[NotMapped]
[DataMember]
public ObjectState ObjectState { get; set; }
#region IDatabaseMetaData Members
[DataMember] public DateTime InsertDatetime { get; set; }
[DataMember] public int InsertSystemUserId { get; set; }
[DataMember] public DateTime? UpdateDatetime { get; set; }
[DataMember] public int? UpdateSystemUserId { get; set; }
public virtual SystemUser InsertSystemUser { get; set; }
public virtual SystemUser UpdateSystemUser { get; set; }
#endregion
}
Here is an implementing class (data contract):
[DataContract(Namespace = Constants.MyNamespace)]
public class AccountClass : EntityBase
{
[DataMember] public int AccountClassId { get; set; }
[DataMember] public string AccountClassCode { get; set; }
[DataMember] public string AccountClassDesc { get; set; }
}
On the client side I have essentially duplicated contracts. Here is the Client.AccountClass:
public class AccountClass : ObjectBase
{
private int _accountClassId;
private string _accountClassCode;
private string _accountClassDesc;
public int AccountClassId
{
get { return _accountClassId;}
set
{
if (_accountClassId == value) return;
_accountClassId = value;
OnPropertyChanged(() => AccountClassId);
}
}
public string AccountClassCode
{
get { return _accountClassCode; }
set
{
if (_accountClassCode == value) return;
_accountClassCode = value;
OnPropertyChanged(() => AccountClassCode);
}
}
public string AccountClassDesc
{
get { return _accountClassDesc; }
set
{
if (_accountClassDesc == value) return;
_accountClassDesc = value;
OnPropertyChanged(() => AccountClassDesc);
}
}
}
..and here is the parts of ObjectBase that matter:
public abstract class ObjectBase : IObjectState, IDatabaseMetaData
{
public ObjectState ObjectState { get; set; }
#region IDatabaseMetaData Members
public DateTime InsertDatetime { get; set; }
public int InsertSystemUserId { get; set; }
public DateTime? UpdateDatetime { get; set; }
public int? UpdateSystemUserId { get; set; }
#endregion
}
When I debug the service in my WcfMessageInspector.BeforeSendReply, I can see the message correctly sending the IObjectState and IDatabaseMetaData values. However, on the client side, they are always null (or default values). I have tried using KnownTypes, applying the namespace to the abstract class. The only way I can serialize everything correctly is to get rid of the interfaces and base classes all together and put the properties directly on the Client/Server AccountClass object. What am I missing here? Thanks.
Update 1
This seems to be a namespace thing. If I move my EntityBase and ObjectBase into the same CLR Namespace, everything works (with no KnownType attributes). In my client contract's AssemblyInfo.cs file I have this:
[assembly: ContractNamespace(Constants.MyNamespace, ClrNamespace = "Project.Name.Client.Entities")]
I tried adding ContractNamespaces here to no avail. Like I said, unless the EntityBase and ObjectBase are in the same namespace, it won't work. However, this is a problem for me because it creates a circular reference, unless I move a lot of stuff around.
Any idea how I can see what the full data contract (namespaces, DataMembers, etc) looks like just before/after serialization on the client/server? I tried intercepting the OnSerializing event without much luck. Thanks again.
This was a namespace issue.
I explicitly add the correct namespace to all parties involved and everything works great. One thing I notice is that the ContractNamespace's ClrNamespace in your AssemblyInfo.cs file should match the AssemblyTitle. Also, putting more than one ContractNamespace in the AssemblyInfo.cs does nothing. For example, I was doing this:
[assembly: ContractNamespace(Constants.MyNamespace, ClrNamespace = "Company.Project.Client.Entities")]
[assembly: ContractNamespace(Constants.MyNamespace, ClrNamespace = "Company.Project.Client.Entities.Core")]
Any POCO in the Company.Project.Client.Entities.Core would not serialize correctly until I explicitly put the DataContract namespace on it like so
[DataContract(Namespace = Constants.MyNamespace)]
public class SomeObject
{
[DataMember] public string SomeProperty { get; set; }
//..etc
}
Alternatively, I could have restructured the project so SomeObject was in the Company.Project.Client.Entities namespace and that would have worked.
Finally, the most helpful thing to debugging this was looking at the WSDL, and then using a custom IDispatchMessageInspector to see the actual messages AfterReceiveRequest and BeforeSendReply. Hopefully this helps someone.

howto initialize a wcf class

Im trying to use a Class in a WCF service. When im calling the
u.attributeChanges.Add(a);
i get:
"Object reference not set to an instance of an object"
If create the classes in the client application it's working.
UpdateChanges Class
[DataContract]
public class UpdateChanges
{
private void Initialize()
{
this.attributeChanges = new List<AttributeChanges>();
}
public UpdateChanges()
{
this.Initialize();
}
[DataMember]
public string objectGuid { get; set; }
[DataMember]
public Utilities.ObjectTypes objectType { get; set; }
[DataMember]
public Utilities.ChangeType changeType{ get; set; }
[DataMember]
public List<AttributeChanges> attributeChanges { get; set; }
[OnDeserializing]
public void OnDeserializing(StreamingContext ctx)
{
this.Initialize();
}
}
AttributeChanges class
[DataContract]
public class AttributeChanges
{
[DataMember]
public string attributeName { get; set; }
[DataMember]
public string attributeValue { get; set; }
}
Client Code:
Service.DirsyncServiceClient proxyClient;
proxyClient = Utilities.GetProxy("http://192.168.1.45/vDir/Service.svc");
Service.UpdateChanges u = new Service.UpdateChanges();
Service.AttributeChanges a = new Service.AttributeChanges();
a.attributeName = "Attribute1";
a.attributeValue = "Value1";
u.attributeChanges.Add(a);
proxyClient.SaveObject(u);
Anyonw know how to solve this?
You're using a generated client code.
The problem is that the client generates this code on base of the WSDL xlm. The code in the CTOR doesn't generated in the client because the client can't be aware of this code.
You have a few options-
1. Use a shared DLL with the data contract instead of generating it via a web reference.
2. Implement it yourself in a 'partial' class.

Parameter xxx of domain operation entry xxx must be one of the predefined serializable types

I get this webservice error sometimes on a SL5 + EF + WCF app.
"Parameter 'role' of domain operation entry 'AddUserPresentationModelToRole' must be one of the predefined serializable types."
here is a similar error, however his solution doesn't work for me.
I have the codegenned DomainService which surfaces the database entities to my client:
[EnableClientAccess()]
public partial class ClientAppDomainService : LinqToEntitiesDomainService<ClientAppUserEntitlementReviewEntities>
{
public IQueryable<Account> GetAccounts()
{
return this.ObjectContext.Accounts;
}
//..etc...
and my custom service which is surfacing a Presentation model, and db entities.
[EnableClientAccess]
[LinqToEntitiesDomainServiceDescriptionProvider(typeof(ClientAppUserEntitlementReviewEntities))]
public class UserColourService : DomainService
{
[Update(UsingCustomMethod = true)]
public void AddUserPresentationModelToRole(UserPresentationModel userPM, Role role, Reviewer reviewer)
{
...
}
public IDictionary<long, byte> GetColourStatesOfUsers(IEnumerable<RBSUser> listOfUsers, string adLogin)
{
//....
}
}
and the PresentationModel:
public class UserPresentationModel
{
[Key]
public long UserID { get; set; }
public byte UserStatusColour { get; set; }
public string MessageText { get; set; }
[Include]
[Association("asdf", "UserID", "UserID")]
public EntityCollection<Account> Accounts { get; set; }
public DateTime AddedDate { get; set; }
public Nullable<long> CostCentreID { get; set; }
public DateTime? DeletedDate { get; set; }
public string EmailAddress { get; set; }
public long EmployeeID { get; set; }
public string FirstName { get; set; }
public Nullable<bool> IsLeaver { get; set; }
public string LastName { get; set; }
public DateTime LastSeenDate { get; set; }
public string LoginDomain { get; set; }
public string LoginName { get; set; }
public byte WorldBuilderStatusID { get; set; }
}
Also cannot get the solution to reliably fail. It seems whenever I change the service slightly ie make it recompile, everything works.
RIAServices unsupported types on hand-built DomainService - seems to be saying the same thing, that decorating the hand built services with the LinqToEntitiesDomainServiceDescriptionProvider should work.
Possible answer here will post back here too with results.
From Colin Blair:
I am a bit surprised it ever works, I don't think I have seen anyone trying to pass additional entiities into a named update before. It might be a bug in RIA Services that it is working at all. What are you trying to accomplish?
Side note, you have a memory leak with your ObjectContext since it is not getting disposed of correctly. Is there a reason you aren't using the LinqToEntitiesDomainSerivce? It would take care of managing the ObjectContext's lifetime for you.
Results:
1) This makes sense. Have refactored out to more sensible parameters now (ints / strings), and all working.
2) Have brought together my 3 separate services into 1 service, which is using the LinqToEntitiesDomainSerivce. The reason I'd split it out before was the assumption that having a CustomUpdate with a PresentationModel didn't work.. and I had to inherit off DomainService instead. I got around this by making a method:
// need this to avoid compile errors for AddUserPresentationModelToRole.. should never be called
public IQueryable<UserPresentationModel> GetUserPresentationModel()
{
return null;
}

WCF with Entity Framework Code First relationship

I'm learning WCF, and tried to make a small service that exposes a Project and its tasks (the standard Entity Framework hello world).
The class structure is the following:
public class Project
{
public int ProjectId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime CreationDate { get; set; }
public virtual ICollection<Task> Tasks { get; set; }
}
public class Task
{
public int TaskId { get; set; }
public string Title { get; set; }
public virtual Project RelatedProject { get; set; }
}
The DB context comes after:
public class ProjectContext : DbContext
{
public DbSet<Project> Projects { get; set; }
public DbSet<Task> Tasks { get; set; }
}
Finally, the service endpoint:
public IEnumerable<Project> getProjects()
{
ProjectContext p = new ProjectContext();
return p.Projects.AsEnumerable();
}
The problem is that this model will throw a System.ServiceModel.CommunicationException, but, If I remove the virtual properties from the model, It would work, but I would loose the entity framework links between Project and Task.
Anyone with a similar setup?
I banged my head against the wall several hours with this one. After extensive debugging, google gave the answer and I feel right to post it here since this was the first result I got in google.
Add this class on top of your [ServiceContract] interface declaration (typically IProjectService.cs
public class ApplyDataContractResolverAttribute : Attribute, IOperationBehavior
{
public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
{
}
public void ApplyClientBehavior(OperationDescription description, System.ServiceModel.Dispatcher.ClientOperation proxy)
{
var dataContractSerializerOperationBehavior =
description.Behaviors.Find<DataContractSerializerOperationBehavior>();
dataContractSerializerOperationBehavior.DataContractResolver =
new ProxyDataContractResolver();
}
public void ApplyDispatchBehavior(OperationDescription description, System.ServiceModel.Dispatcher.DispatchOperation dispatch)
{
var dataContractSerializerOperationBehavior =
description.Behaviors.Find<DataContractSerializerOperationBehavior>();
dataContractSerializerOperationBehavior.DataContractResolver =
new ProxyDataContractResolver();
}
public void Validate(OperationDescription description)
{
// Do validation.
}
}
Requirements are
using System.ServiceModel.Description;
using System.Data.Objects;
using System.ServiceModel.Channels;
Then under the [OperationContract] keyword add [ApplyDataContractResolver] keyword and you are set!
Big thanks to http://blog.rsuter.com/?p=286
For sending data trough WCF you should disable lazy loading (dataContext.ContextOptions.LazyLoadingEnabled = false;).
To be sure the data you want is loaded you need to use eager loading ( trough the Include method).
You need to change your function to:
public IEnumerable<Project> getProjects()
{
ProjectContext p = new ProjectContext();
p.ContextOptions.LazyLoadingEnabled = false;
return p.Projects.Include("Tasks").AsEnumerable();
}

Issue With Fluent Nhibernate Automapping and Guids / UniqueIdentifiers as Primary Key Fields

I am attempting to use the Fluent-NHibernate automapping functionality (in the latest version of the software) and am running into problems using Guids as the Primary Key fields. If I use integer fields for the primary keys, the tables are generated successfully and all Nhibernate functionality seems to work fine. FYI, I am using NHibernate to generate my database tables.
Here are a couple of classes with integer IDs.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
namespace Sample.Data.Entities
{
public class Employee
{
public virtual int Id { get; private set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual Store Store { get; set; }
}
public class Product
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual double Price { get; set; }
public virtual IList<Store> StoresStockedIn { get; private set; }
public Product()
{
StoresStockedIn = new List<Store>();
}
}
public class Store
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual IList<Product> Products { get; set; }
public virtual IList<Employee> Staff { get; set; }
public Store()
{
Products = new List<Product>();
Staff = new List<Employee>();
}
public virtual void AddProduct(Product product)
{
product.StoresStockedIn.Add(this);
Products.Add(product);
}
public virtual void AddEmployee(Employee employee)
{
employee.Store = this;
Staff.Add(employee);
}
}
}
Here are the same classes with GUIDs.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
namespace Sample.Data.Entities
{
public class Employee
{
public virtual Guid Id { get; private set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual Store Store { get; set; }
}
public class Product
{
public virtual Guid Id { get; private set; }
public virtual string Name { get; set; }
public virtual double Price { get; set; }
public virtual IList<Store> StoresStockedIn { get; private set; }
public Product()
{
StoresStockedIn = new List<Store>();
}
}
public class Store
{
public virtual Guid Id { get; private set; }
public virtual string Name { get; set; }
public virtual IList<Product> Products { get; set; }
public virtual IList<Employee> Staff { get; set; }
public Store()
{
Products = new List<Product>();
Staff = new List<Employee>();
}
public virtual void AddProduct(Product product)
{
product.StoresStockedIn.Add(this);
Products.Add(product);
}
public virtual void AddEmployee(Employee employee)
{
employee.Store = this;
Staff.Add(employee);
}
}
}
Here is my configuration.
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(c => c.FromConnectionStringWithKey("AAAConnectionString"))
.UseReflectionOptimizer()
.AdoNetBatchSize(25)
.DefaultSchema("dbo")
.Cache(c => c
.UseQueryCache()
.ProviderClass<HashtableCacheProvider>())
.ShowSql())
.Mappings(m=>m.AutoMappings
.Add(AutoMap.AssemblyOf<Sample.Data.Entities.Product>()
.Where(type => type.Namespace == "Sample.Data.Entities.Product")
.Conventions.AddFromAssemblyOf<Sample.Data.Fluent.Conventions.PrimaryKeyNameConvention>()
))
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory();
To work around the issue, I attempted to generate conventions (see below) for 1) naming the Id field (although I thought it should have been unnecessary) and for 2) generating the Id (which I thought would have been automatic). I am unsure what is happening or why this is not working.
public class PrimaryKeyNameConvention : IIdConvention
{
public bool Accept(IIdentityInstance id)
{
return true;
}
public void Apply(IIdentityInstance id)
{
id.Column("Id");
}
}
public class PrimaryKeyGeneratorConvention : IIdConvention
{
public bool Accept(IIdentityInstance id)
{
return true;
}
public void Apply(IIdentityInstance id)
{
id.GeneratedBy.GuidComb();
}
}
Also, if I turn automapping off and use Fluently configured map the tables are generated successfully.
This is driving me nuts, and I am sure it is probably a quick fix. Any ideas?
Thank you!
Anthony
Apparently, there was an issue in the Fluent Nhibernate version 1.0RC and version 1.0. However, if you download the latest version from the SVN trunk, every thing works perfectly. It appears that the problem was simply a bug in the code which has now been corrected.
Also, I should note that James Gregory, Paul Batum, and perhaps others, are actively working on Fluent NHibernate. The product is evolving pretty dramatically and there have been significant changes to the code over the last couple of months.