WCF: Return type should implement abstract method.
-
Hi All, I think I'm just being stupid and I need to add an attribute to a method but despite endless googling I can still not find the answer so I thought I'd try my luck here. I have a abstract class called Discount, Discount has an abstract method called Calculate: [DataContract] [KnownType(typeof(Percentage))] public abstract class Discount : IDiscount { //Some data members here public abstract void Calculate(); } Here is the concrete implementation: [DataContract] internal class Percentage:Discount { internal Percentage() { } public override void Calculate() { //Logic here } } Now in the interface for the WCF project a generic type of Discount is returned (so I can return different implementations of discount), and this knows that it should implement a method called Calculate(). However because WCF is opt in with reagrds to data it doesn't yet know about the Calculate implementation. So I get this error: Error 1 'ECommerce_TestSuite.ECommerce_API.Percentage' does not implement inherited abstract member 'ECommerce_Module.BusinessObject.Discount.Discount.Calculate(ECommerce_Module.VirtualObject.IBasket)' So I think I just need to add an attribute to either the implementation of the Calculate method or to the abstract method declarations so that the WCF project knows that it does have an implemented method. However I do not know what attribute this is! Any help would be really appreciated. Phil
-
Hi All, I think I'm just being stupid and I need to add an attribute to a method but despite endless googling I can still not find the answer so I thought I'd try my luck here. I have a abstract class called Discount, Discount has an abstract method called Calculate: [DataContract] [KnownType(typeof(Percentage))] public abstract class Discount : IDiscount { //Some data members here public abstract void Calculate(); } Here is the concrete implementation: [DataContract] internal class Percentage:Discount { internal Percentage() { } public override void Calculate() { //Logic here } } Now in the interface for the WCF project a generic type of Discount is returned (so I can return different implementations of discount), and this knows that it should implement a method called Calculate(). However because WCF is opt in with reagrds to data it doesn't yet know about the Calculate implementation. So I get this error: Error 1 'ECommerce_TestSuite.ECommerce_API.Percentage' does not implement inherited abstract member 'ECommerce_Module.BusinessObject.Discount.Discount.Calculate(ECommerce_Module.VirtualObject.IBasket)' So I think I just need to add an attribute to either the implementation of the Calculate method or to the abstract method declarations so that the WCF project knows that it does have an implemented method. However I do not know what attribute this is! Any help would be really appreciated. Phil
I think you just need to put the
[DataMember]
attribute on any property/method that WCF should recognize. This is in addition to the[DataContract]
attribute you already have on the class.Proud to have finally moved to the A-Ark. Which one are you in?
Author of the Guardians Saga (Sci-Fi/Fantasy novels) -
I think you just need to put the
[DataMember]
attribute on any property/method that WCF should recognize. This is in addition to the[DataContract]
attribute you already have on the class.Proud to have finally moved to the A-Ark. Which one are you in?
Author of the Guardians Saga (Sci-Fi/Fantasy novels)Nope, Error 1 Attribute 'DataMember' is not valid on this declaration type. It is only valid on 'property, indexer, field' declarations.
-
Nope, Error 1 Attribute 'DataMember' is not valid on this declaration type. It is only valid on 'property, indexer, field' declarations.
To clarify... Are you getting this error at runtime or compile time? EDIT: I'm guessing you trimmed down the code to post, as I notice your error message shows Calculate() having an argument. If this is a compile-time error, then it has nothing to do with WCF, and probably indicates that you just need to double-check the arguments in the abstract and override versions of Calculate to be sure that they match.
Proud to have finally moved to the A-Ark. Which one are you in?
Author of the Guardians Saga (Sci-Fi/Fantasy novels) -
To clarify... Are you getting this error at runtime or compile time? EDIT: I'm guessing you trimmed down the code to post, as I notice your error message shows Calculate() having an argument. If this is a compile-time error, then it has nothing to do with WCF, and probably indicates that you just need to double-check the arguments in the abstract and override versions of Calculate to be sure that they match.
Proud to have finally moved to the A-Ark. Which one are you in?
Author of the Guardians Saga (Sci-Fi/Fantasy novels)Sorry forgot to say, its a compile time error.
-
Sorry forgot to say, its a compile time error.
Ah, then like I said, this actually isn't related to WCF at all. Could you post the actual function definition for Calculate in both the abstract class and the subclass?
Proud to have finally moved to the A-Ark. Which one are you in?
Author of the Guardians Saga (Sci-Fi/Fantasy novels) -
Ah, then like I said, this actually isn't related to WCF at all. Could you post the actual function definition for Calculate in both the abstract class and the subclass?
Proud to have finally moved to the A-Ark. Which one are you in?
Author of the Guardians Saga (Sci-Fi/Fantasy novels)It must be related to WCF as this error occurs in the test project were the service reference is. In the implementation the project compiles fine. The workaround I have at the moment is to make the Calculate method in the abstract class virtual. Then because it knows a type Discount is returned it will compile and all is well. However this should be an abstract method really I just need to tell the WCF project that this method is implemented in the concrete class Percentage somehow?
-
It must be related to WCF as this error occurs in the test project were the service reference is. In the implementation the project compiles fine. The workaround I have at the moment is to make the Calculate method in the abstract class virtual. Then because it knows a type Discount is returned it will compile and all is well. However this should be an abstract method really I just need to tell the WCF project that this method is implemented in the concrete class Percentage somehow?
Do me a favor and post the method signatures for both versions of Calculate() in both projects... Let's make sure it all matches up.
Proud to have finally moved to the A-Ark. Which one are you in?
Author of the Guardians Saga (Sci-Fi/Fantasy novels) -
Do me a favor and post the method signatures for both versions of Calculate() in both projects... Let's make sure it all matches up.
Proud to have finally moved to the A-Ark. Which one are you in?
Author of the Guardians Saga (Sci-Fi/Fantasy novels)Ok: How I've got it now: [DataContract] [Serializable] public abstract class Discount : IDiscount { #region Constructors internal Discount() { } #endregion //Should be abstract but then WCF engine will not compile public virtual void Calculate(VirtualObject.IBasket basket) { throw new NotImplementedException(); } #region Helper Methods protected decimal RoundToTwoDecimalPlaces(decimal amount) { return (Math.Ceiling(amount * 100) / 100); } #endregion } This compiles and works but really it should be abstract. So how it should be: [DataContract] [Serializable] public abstract class Discount : IDiscount { #region Constructors internal Discount() { } #endregion public abstract void Calculate(VirtualObject.IBasket basket); } And this is how its implemented in the concrete class Percentage: [DataContract] [Serializable] internal class Percentage:Discount { internal Percentage() { } public override void Calculate(VirtualObject.IBasket basket) { foreach (ECommerce_Module.VirtualObject.IBasketItem item in basket.Items) { decimal productPrice = item.ItemPricePreDiscount.Value; item.ItemPricePostDiscount = (item.ItemPricePreDiscount - RoundToTwoDecimalPlaces(productPrice * (DiscountValue.Value / 100)) * item.Quantity); } } } But this the returns the error: Error 2 'ECommerce_TestSuite.ECommerce_API.Percentage' does not implement inherited abstract member 'ECommerce_Module.BusinessObject.Discount.Discount.Calculate(ECommerce_Module.VirtualObject.IBasket)' ..\ECommerce_Module\v1\DotNet\ECommerce_TestSuite\Service References\ECommerce_API\Reference.cs 51 26 ECommerce_TestSuite This error is in the test suite, where ECommerce_API is the service reference to the WCF project. So that tells me that the WCF project doesn't know that the method Calculate is implemented in the concrete class because I haven't "opted" to expose the method, but how I do this I have no idea! Thanks for the help so far though
-
Ok: How I've got it now: [DataContract] [Serializable] public abstract class Discount : IDiscount { #region Constructors internal Discount() { } #endregion //Should be abstract but then WCF engine will not compile public virtual void Calculate(VirtualObject.IBasket basket) { throw new NotImplementedException(); } #region Helper Methods protected decimal RoundToTwoDecimalPlaces(decimal amount) { return (Math.Ceiling(amount * 100) / 100); } #endregion } This compiles and works but really it should be abstract. So how it should be: [DataContract] [Serializable] public abstract class Discount : IDiscount { #region Constructors internal Discount() { } #endregion public abstract void Calculate(VirtualObject.IBasket basket); } And this is how its implemented in the concrete class Percentage: [DataContract] [Serializable] internal class Percentage:Discount { internal Percentage() { } public override void Calculate(VirtualObject.IBasket basket) { foreach (ECommerce_Module.VirtualObject.IBasketItem item in basket.Items) { decimal productPrice = item.ItemPricePreDiscount.Value; item.ItemPricePostDiscount = (item.ItemPricePreDiscount - RoundToTwoDecimalPlaces(productPrice * (DiscountValue.Value / 100)) * item.Quantity); } } } But this the returns the error: Error 2 'ECommerce_TestSuite.ECommerce_API.Percentage' does not implement inherited abstract member 'ECommerce_Module.BusinessObject.Discount.Discount.Calculate(ECommerce_Module.VirtualObject.IBasket)' ..\ECommerce_Module\v1\DotNet\ECommerce_TestSuite\Service References\ECommerce_API\Reference.cs 51 26 ECommerce_TestSuite This error is in the test suite, where ECommerce_API is the service reference to the WCF project. So that tells me that the WCF project doesn't know that the method Calculate is implemented in the concrete class because I haven't "opted" to expose the method, but how I do this I have no idea! Thanks for the help so far though
Ok, this might be a silly question, but let's just rule out anything you might have missed (Sometimes we get too close to the problem and start missing things that are "too obvious")... Does the test suite, where the compile-time error is occurring, reference these classes from the same library, or is it using a copy? If it's a copy, check the implementation of 'Percentage' in the test suite to make sure Calcluate() is implemented. Remember that while ServiceContract classes only have to be implemented on the server side, with an interface given to the client, DataContract classes are just passed as-is (The attributes only specify which fields/properties are serialized). Any code in a DataContract class has to be available to the client.
Proud to have finally moved to the A-Ark. Which one are you in?
Author of the Guardians Saga (Sci-Fi/Fantasy novels) -
Ok, this might be a silly question, but let's just rule out anything you might have missed (Sometimes we get too close to the problem and start missing things that are "too obvious")... Does the test suite, where the compile-time error is occurring, reference these classes from the same library, or is it using a copy? If it's a copy, check the implementation of 'Percentage' in the test suite to make sure Calcluate() is implemented. Remember that while ServiceContract classes only have to be implemented on the server side, with an interface given to the client, DataContract classes are just passed as-is (The attributes only specify which fields/properties are serialized). Any code in a DataContract class has to be available to the client.
Proud to have finally moved to the A-Ark. Which one are you in?
Author of the Guardians Saga (Sci-Fi/Fantasy novels)Ian Shlasko wrote:
Does the test suite, where the compile-time error is occurring, reference these classes from the same library, or is it using a copy? If it's a copy, check the implementation of 'Percentage' in the test suite to make sure Calcluate() is implemented.
Hmm, I've just done this in the test suite: ECommerce_API.Percentage percentage = new ECommerce_API.Percentage(); So that's me calling the Percentage class from the Service Reference (called ECommerece_API). This class has a method called Calculate which I can call so the IDE knows that its implemented. Its just the reference.cs file that's having trouble??
-
Ian Shlasko wrote:
Does the test suite, where the compile-time error is occurring, reference these classes from the same library, or is it using a copy? If it's a copy, check the implementation of 'Percentage' in the test suite to make sure Calcluate() is implemented.
Hmm, I've just done this in the test suite: ECommerce_API.Percentage percentage = new ECommerce_API.Percentage(); So that's me calling the Percentage class from the Service Reference (called ECommerece_API). This class has a method called Calculate which I can call so the IDE knows that its implemented. Its just the reference.cs file that's having trouble??
The IDE knows that it's a subclass of Discount, so it could be showing you the Calculate() from the parent class. It doesn't necessarily know whether Percentage has it implemented. I'm trying to eliminate the possibility that you're calling the wrong "Percentage" class, or the wrong version of it. So do you have one common implementation of Percentage that both the server and client reference (As in, are they both pointing to the same DLL), or do you have two copies of it, one for each? Try temporarily adding a simple
public void Test() { }
to Percentage, and see if you can call it from the test suite.Proud to have finally moved to the A-Ark. Which one are you in?
Author of the Guardians Saga (Sci-Fi/Fantasy novels) -
The IDE knows that it's a subclass of Discount, so it could be showing you the Calculate() from the parent class. It doesn't necessarily know whether Percentage has it implemented. I'm trying to eliminate the possibility that you're calling the wrong "Percentage" class, or the wrong version of it. So do you have one common implementation of Percentage that both the server and client reference (As in, are they both pointing to the same DLL), or do you have two copies of it, one for each? Try temporarily adding a simple
public void Test() { }
to Percentage, and see if you can call it from the test suite.Proud to have finally moved to the A-Ark. Which one are you in?
Author of the Guardians Saga (Sci-Fi/Fantasy novels)Hmm, just added Test() to the percentage class and the test suite could not see it. So that would that support my argument that I need to expose the Calculate method in Percentage to the WCF so that it knows its implemented?
-
Hmm, just added Test() to the percentage class and the test suite could not see it. So that would that support my argument that I need to expose the Calculate method in Percentage to the WCF so that it knows its implemented?
Again, this has nothing to do with WCF, since it's compile-time. WCF basically does two things, in this context: 1) Allows ServiceContract classes, and associated callbacks, to call methods across a remote connection 2) Automatically serializes and de-serializes DataContract classes Anything in a DataContract class needs to have a visible implementation on the client side. No method calls in a DataContract class go over the network, so the client has to have a full implementation. If this is meant to run on the server side, it needs to be part of your ServiceContract class.
Proud to have finally moved to the A-Ark. Which one are you in?
Author of the Guardians Saga (Sci-Fi/Fantasy novels) -
Again, this has nothing to do with WCF, since it's compile-time. WCF basically does two things, in this context: 1) Allows ServiceContract classes, and associated callbacks, to call methods across a remote connection 2) Automatically serializes and de-serializes DataContract classes Anything in a DataContract class needs to have a visible implementation on the client side. No method calls in a DataContract class go over the network, so the client has to have a full implementation. If this is meant to run on the server side, it needs to be part of your ServiceContract class.
Proud to have finally moved to the A-Ark. Which one are you in?
Author of the Guardians Saga (Sci-Fi/Fantasy novels)Ian Shlasko wrote:
Anything in a DataContract class needs to have a visible implementation on the client side. No method calls in a DataContract class go over the network, so the client has to have a full implementation. If this is meant to run on the server side, it needs to be part of your ServiceContract class.
Ok, so the Calculate method of Percentage should be run server side not client side. So how can I add this method to the Service Contract? This method is only called when a different method which is in the service contract is called. Its almost at the wrong level allow me to explain: The methods that are in the service contract are for manipulating a shopping cart. So AddItem, AddPromotionCode, SetDelivery, ViewBasket etc etc. The calculate method is a function that is called when the ViewBasket operation is called. This then checks to see if a promotion is applied and if true, what the value of the discount is. So this calculate method is an internal function and shouldn't be possible to be called directly. And its a functions of the basket not the shopping cart. All the methods in the service contract are for manipulating the shopping cart, not the basket directly. So Shopping Cart -- Service Contract at this level | | \/ Basket -- which has the Calculate method So if I add Calculate to the Service Contract i'm kind of breaking the architecture of the solution :/
-
Ian Shlasko wrote:
Anything in a DataContract class needs to have a visible implementation on the client side. No method calls in a DataContract class go over the network, so the client has to have a full implementation. If this is meant to run on the server side, it needs to be part of your ServiceContract class.
Ok, so the Calculate method of Percentage should be run server side not client side. So how can I add this method to the Service Contract? This method is only called when a different method which is in the service contract is called. Its almost at the wrong level allow me to explain: The methods that are in the service contract are for manipulating a shopping cart. So AddItem, AddPromotionCode, SetDelivery, ViewBasket etc etc. The calculate method is a function that is called when the ViewBasket operation is called. This then checks to see if a promotion is applied and if true, what the value of the discount is. So this calculate method is an internal function and shouldn't be possible to be called directly. And its a functions of the basket not the shopping cart. All the methods in the service contract are for manipulating the shopping cart, not the basket directly. So Shopping Cart -- Service Contract at this level | | \/ Basket -- which has the Calculate method So if I add Calculate to the Service Contract i'm kind of breaking the architecture of the solution :/
My suggestion would be to separate the business logic (
Calculate()
) from the data package (Basket). Keep your DataContract classes as code-free as possible... Just properties, fields, and formatting/transformation methods. Instead of putting a Calculate there, maybe give the server a BasketCalculator static class...BasketCalculator.Calculate(myBasket);
Either that, or have the server work with a rich, self-calculating Basket class, but actually send a simplified version to the client (Copy the data to a lightweight DataContract object). The client would just get the equivalent of a database record, with only the data it needs to display.Proud to have finally moved to the A-Ark. Which one are you in?
Author of the Guardians Saga (Sci-Fi/Fantasy novels) -
My suggestion would be to separate the business logic (
Calculate()
) from the data package (Basket). Keep your DataContract classes as code-free as possible... Just properties, fields, and formatting/transformation methods. Instead of putting a Calculate there, maybe give the server a BasketCalculator static class...BasketCalculator.Calculate(myBasket);
Either that, or have the server work with a rich, self-calculating Basket class, but actually send a simplified version to the client (Copy the data to a lightweight DataContract object). The client would just get the equivalent of a database record, with only the data it needs to display.Proud to have finally moved to the A-Ark. Which one are you in?
Author of the Guardians Saga (Sci-Fi/Fantasy novels)Ok, I'll give that a go, thanks for the help Ian, many Kudos!