- Home
- LLBLGen Pro
- Architecture
Interfaces, Inheritance and WCF
Joined: 04-Aug-2006
Hi LLBLGEN folks!
I have been doing a few experiments with LLBLGEN and WCF and have come across something a bit odd.
I have created an interfaces for my customer entity:
eg
public interface ICustomer
{
int Id { get; }
string AltCode { get; set; }
string Code { get; set; }
IList ChildCustomers { get; }
StorageItem ParentCustomer { get; }
}
then created my own entity that is inherited from the CustomerEntity in the LLBLGEN code that implements the interface:
public class Customer : CustomerEntity, ISerializable, ICustomer
{
public Customer() { }
public Customer(IEntityFields2 fields) : base(fields) { }
public Customer(System.Int32 id) : base(id) { }
protected Customer(SerializationInfo info, StreamingContext context) : base(info, context) { }
/// <summary>Creates a new instance of the factory related to this entity</summary>
protected override IEntityFactory2 CreateEntityFactory()
{
return new CustomerFactory();
}
/// <summary>
/// Creates a new PrefetchPathElement2 object which contains all the information to prefetch the related entities of type 'Customer'
/// for this entity. Add the object returned by this property to an existing PrefetchPath2 instance.
/// </summary>
/// <returns>Ready to use IPrefetchPathElement2 implementation.</returns>
public new static IPrefetchPathElement2 PrefetchPathChildCustomers
{
get { return new PrefetchPathElement2(new EntityCollection(new CustomerFactory()), CustomerEntity.Relations.CustomerEntityUsingParentId, (int)EntityType.CustomerEntity, (int)EntityType.CustomerEntity, 0, null, null, null, null, "ChildCustomers", SD.LLBLGen.Pro.ORMSupportClasses.RelationType.OneToMany); }
}
/// <summary>
/// Creates a new PrefetchPathElement2 object which contains all the information to prefetch the related entities of type 'Customer'
/// for this entity. Add the object returned by this property to an existing PrefetchPath2 instance.
/// </summary>
/// <returns>Ready to use IPrefetchPathElement2 implementation.</returns>
public new static IPrefetchPathElement2 PrefetchPathParentCustomer
{
get { return new PrefetchPathElement2(new EntityCollection(new CustomerFactory()), CustomerEntity.Relations.CustomerEntityUsingIdParentId, (int)EntityType.CustomerEntity, (int)EntityType.CustomerEntity, 0, null, null, null, null, "ParentCustomer", SD.LLBLGen.Pro.ORMSupportClasses.RelationType.ManyToOne); }
}
/// <summary>
/// Gets the EntityCollection with the related entities of type 'Customer' which are related to this entity via a relation of type '1:n'.
/// If the EntityCollection hasn't been fetched yet, the collection returned will be empty.
/// </summary>
public new IList ChildCustomers
{
get
{
EntityCollection<CustomerEntity> toReturn = base.ChildCustomers;
toReturn.EntityFactoryToUse = new CustomerFactory();
return (IList) toReturn;
}
}
/// <summary>
/// Gets / sets related entity of type 'MyCustomerEntity' which has to be set using a fetch action earlier. If no related entity
/// is set for this property, null is returned.
/// This property is not visible in databinded grids.
/// </summary>
public new virtual ICustomer ParentCustomer
{
get { return (Customer)base.ParentCustomer; }
set { base.ParentCustomer = (Customer) value; }
}
}
internal class CustomerFactory : CustomerEntityFactory
{
/// <summary>returns the name of the entity this factory is for, "UserEnergy"</summary>
public override string ForEntityName
{
get { return "Customer"; }
}
public override IEntity2 Create(IEntityFields2 fields)
{
return new Customer(fields);
}
}
}
My WCf code fetches the entity by its ID:
public ICustomer GetCustomer(int id)
{
if (id == 0) { throw new ArgumentException("id cannot be zero", "id"); }
Customer Entity = new Customer();
PrefetchPath2 Path = new PrefetchPath2(EntityType.CustomerEntity);
Path.Add(Customer.PrefetchPathChildCustomers);
Path.Add(Customer.PrefetchPathParentCustomer);
using (DataAccessAdapter Adapter = new DataAccessAdapter())
{
Adapter.FetchEntityUsingUniqueConstraint(Entity, new PredicateExpression(CustomerFields.Id == id), Path, null);
}
return (ICustomer) Entity;
}
[ServiceContract]
[ServiceKnownType(typeof(Customer))]
public interface ICustomerService
{
[OperationContract]
ICustomer GetCustomer(int id);
}
When I fetch the customer from the WCF Client I recieve a null reference exception, but the info in it is fairly useless (hence why I haven't included it).
When I change this bit of code on the entity and update the interface to match it works:
this:
public new virtual ICustomer ParentCustomer
{
get { return (Customer)base.ParentCustomer; }
set { base.ParentCustomer = (Customer) value; }
}
to this:
public new virtual Customer ParentCustomer
{
get { return (Customer) base.ParentCustomer; }
set { base.ParentCustomer = (Customer) value; }
}
I assume the error is something to do with the serialiser having issues with the interface. The error only happens when the parent Customer actually contains something.
Any ideas how to get the entity to implement the interface and have it work with WCF?
Sorry if this is more a WCF question than a LLBLGEN one.
Thanks! Alex
Joined: 21-Aug-2005
I think it might not be possible.
MSDN wrote:
The serialization subsystem is not capable of creating an XML representation for the interface. Remember that any parameter or return type specified in an OperationContract must be either annotated with DataContract or Serializable (or IXmlSerializablem, etc), so, an Interface cannot be used in this case.
ref: http://66.102.9.104/search?q=cache:ULNvdTME_QcJ:forums.microsoft.com/MSDN/ShowPost.aspx %3FPostID%3D2820467%26SiteID%3D1+ServiceKnownType+Interface&hl=en&ct=clnk&cd=6
That's from google's cache as the original link displays some error.
Joined: 04-Aug-2006
Hi Walaa,
I seem to have got it working with some dummy classes (not based on LLBLGen entities) using the NetDataContractAttribute.
eg:
public class NetDataContractAttribute : Attribute, IOperationBehavior
{
public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
{
}
public void ApplyClientBehavior(OperationDescription description, ClientOperation proxy)
{
ReplaceDataContractSerializerOperationBehavior(description);
}
public void ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch)
{
ReplaceDataContractSerializerOperationBehavior(description);
}
public void Validate(OperationDescription description)
{
}
private static void ReplaceDataContractSerializerOperationBehavior(OperationDescription description)
{
DataContractSerializerOperationBehavior dcsOperationBehavior = description.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (dcsOperationBehavior != null)
{
description.Behaviors.Remove(dcsOperationBehavior);
description.Behaviors.Add(new NetDataContractSerializerOperationBehavior(description));
}
}
public class NetDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior
{
public NetDataContractSerializerOperationBehavior(OperationDescription operationDescription)
: base(operationDescription)
{
}
public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
{
return new NetDataContractSerializer();
}
public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
{
return new NetDataContractSerializer();
}
}
}
I found this code on this site: http://www.thoughtshapes.com/WCF/UsingInterfacesAsParametersTwo.htm after researching the issue further.
using it involves altering the service contract like so and adding the netdatacontract attribute:
[OperationContract]
[NetDataContract]
ICustomer GetCustomer(string customerName);
Works fine for any class I create that are not based on the LLBGEN entities, but the LLBLGEN ones just error when fetching the entity over the WCF service. The downside with this solution is it wont work if the client isn't .net based.
Any ideas why having the parent property would cause the LLBLGen entity to fall over?
Cheers! Alex
Joined: 21-Aug-2005
Just a notice:
public interface ICustomer { int Id { get; } string AltCode { get; set; } string Code { get; set; } IList ChildCustomers { get; } **StorageItem ParentCustomer **{ get; } }
Shouldn't the ParentCustomer be defined of type ICustomer or Customer in the Interface?
Joined: 04-Aug-2006
Yep sorry thats a typo should be:
public interface ICustomer { int Id { get; } string AltCode { get; set; } string Code { get; set; } IList ChildCustomers { get; } ICustomer ParentCustomer { get; } }
How is serialization handled for the LLBLgen entities?
Thanks A