Solved

Add fields to a wcf web service

Posted on 2015-01-30
18
128 Views
Last Modified: 2015-02-02
I've built a running WCF web service in C#, and a demo client to consume the service. My web service returns three fields (score, description, ranking) based on a stored procedure query to a SQL Server db.

I need to add a fourth field, IncentiveLevel, that is based partly on score and partly on business rules.  How do I incorporate this into the results class? Here is the relevant code from the services1.svc file:
 public List<TopCrossSells> GetTopCrossSells(string CustomerId, string ProductCategory)
        {
            List<TopCrossSells> results = new List<TopCrossSells>();
            LongbowDemoDataContext dc = new LongbowDemoDataContext();
            int CustomerIdNumber = int.Parse(CustomerId);
            double? Score = 0;
            string ClientKey = "";
            int? Ranking = 1;

            foreach (uspGetTopCrossSellsResult Scores
             in dc.uspGetTopCrossSells(CustomerIdNumber, ProductCategory, ref Score, ref ClientKey, ref Ranking))
            {
                results.Add(new TopCrossSells()
                {
                    Score = (float)Scores.@Score,
                    ClientKey = Scores.@ClientKey,
                    Ranking = Scores.@Ranking
                });
            }
            return results;
        }        
        

Open in new window

0
Comment
Question by:Mark Klein
  • 11
  • 7
18 Comments
 
LVL 35

Expert Comment

by:Miguel Oz
ID: 40580752
You need to find the TopCrossSells class declaration and add a public property as follows:
public class TopCrossSells //existing declaration
{
        //Existing properties here no change.
       [DataMember]
        public int IncentiveLevel; //Assuming is integer type  but just change accordingly

}

Open in new window


Notice that to consume this new property, the following additional changes are required.

1.

uspGetTopCrossSellsResult class must contain an extra property (IncentiveLevel)

2.

uspGetTopCrossSells method (assuming this one calls store procedure) must assign extra property defined in previous step.

3.

The constructor of result class must assign extra property value from Scores instance.
0
 

Author Comment

by:Mark Klein
ID: 40580868
I totally agree with step 1.  This is along the lines I've been considering.

Step 2 leaves me confused. Incentive level, while based in part on score, is not in the db.  Here's the proc:
USE [longbowdemo]
GO
/****** Object:  StoredProcedure [dbo].[uspGetTopCrossSells]    Script Date: 01/30/2015 18:10:52 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:		<Mark Klein>
-- Create date: <January 17, 2015>
-- Description:	<Top cross sells for a given customer,>
-- =============================================
ALTER PROCEDURE [dbo].[uspGetTopCrossSells] 
	-- Add the parameters for the stored procedure here
	@customerId int, @ProductCategory nvarchar (50),
	@Score float Output, @ClientKey nvarchar (50) Output, @Ranking int Output
	
AS
BEGIN
	-- SET NOCOUNT ON added to prevent extra result sets from
	-- interfering with SELECT statements.
	SET NOCOUNT ON;

    -- Insert statements for procedure here
	SELECT Score, ClientKey, Ranking
	FROM CrossSellScore css, ItemCategory ic
	WHERE customerid = @customerid
       and score > 6
       and css.ItemCategoryId = ic.Id
       and description like @ProductCategory+'|%'
    Order By Score Desc
END

Open in new window


I think if I get step 2 figured out, I could work my way through step 3. My plan is to put the business logic that determines the incentive level (based on Score) in another class library called by the service layer.
0
 
LVL 35

Expert Comment

by:Miguel Oz
ID: 40580874
If it is not in the database where is supposed to be?
For example, assuming there is no need to change the database, If incentive level is calculated in a C# method (say CalcIncentiveLevel(uspGetTopCrossSellsResult  score)) then you can skip step 2 and use this method in step 3 to assign this value to the required property.
Note: IF more help needed. Please post uspGetTopCrossSellsResult class declaration. Is this class auto generated? If so how? EF? Which version?
0
 

Author Comment

by:Mark Klein
ID: 40580928
Miguel, I'm an 80+ years old newbie back to programming after literally 50 years, so I may make some dumb statements.
I'm using Linq to Sql, and the uspGetTopCrossSellResult was auto generated when I dragged the stored proc from the Server Explorer window to the design surface. Here's what was created:
public partial class uspGetTopCrossSellsResult
	{
		
		private double _Score;
		
		private string _ClientKey;
		
		private System.Nullable<int> _Ranking;
		
		public uspGetTopCrossSellsResult()
		{
		}
		
		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Score", DbType="Float NOT NULL")]
		public double Score
		{
			get
			{
				return this._Score;
			}
			set
			{
				if ((this._Score != value))
				{
					this._Score = value;
				}
			}
		}
		
		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ClientKey", DbType="NVarChar(300) NOT NULL", CanBeNull=false)]
		public string ClientKey
		{
			get
			{
				return this._ClientKey;
			}
			set
			{
				if ((this._ClientKey != value))
				{
					this._ClientKey = value;
				}
			}
		}
		
		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Ranking", DbType="Int")]
		public System.Nullable<int> Ranking
		{
get
			{
				return this._Ranking;
			}
			set
			{
				if ((this._Ranking != value))
				{
					this._Ranking = value;
				}
			}
		}
	}
}

Open in new window

0
 
LVL 35

Expert Comment

by:Miguel Oz
ID: 40581102
No worries, notice that the autogenerated code is just a nice wrapper of the 3 SP outputs .
One question still remains, if incentive level is not in DB. Where is it calculated?
If it is in the business logic then my previous comment is still valid "If incentive level is calculated in a C# method (say CalcIncentiveLevel(uspGetTopCrossSellsResult  score)) then you can skip step 2 and use this method in step 3 to assign this value to the required property."
You just need to find out where to locate this method. I am not familiar (have enough details) with your business logic to provide more  insights.
0
 

Author Comment

by:Mark Klein
ID: 40581212
OK, here are some details. Ultimately I'd like to implement business rules like this to calculate IncentiveLevel:
For starters,
Cross-sell score      >9.0       9.0 >= score > 8.0       8.0>= score > 7.0       7.0 >= score
Incentive level      none      10%                                              15%                       20%

But then I want to get to a rule with more variables such as the attached Excel file
cross-sell-business-rules.xlsx
0
 

Author Comment

by:Mark Klein
ID: 40581214
Both score and risk score are in the db. My stored proc can be expanded to also return riskscore.
I'd like to keep the business logic in another layer, say in another project in my solution
0
 

Author Comment

by:Mark Klein
ID: 40581223
I've done the very easy part, adding IncentiveLevel to the TopCrossSell class.

I'm unsure about the additions to the uspGetTopCrossSellsResult.  I can add to the public partial class list easily enough, but when adding a section to the public...(), I'm concerned about code like

[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ClientKey", DbType="NVarChar(300) NOT NULL", CanBeNull=false)]
            public string ClientKey

There is no such ColumnAttribute in the db for IncentiveLevel.  What do I add here?
Then there is the tough part, at least for me, of assigning the new property value to the results class. I'm digging into it.  I'm thinking of a new project and class with a big If/then statement to determine incentivelevel.
0
 

Author Comment

by:Mark Klein
ID: 40581847
In order to evaluate the rules I'm thinking of supporting, my web service needs access to a collection of customer metrics.  The {scores, description, ranking} values come from a table CrossSellScores where "score" is cross-sell probability, roughly. Other customer metrics come from a different table, CustomerScores. I'll need to know a collection of metrics (for example, Recency, RiskScore, Revenue) for a given customer from the Customer Scores table in order to evaluate a business rule to calculate an incentiveLevel that might be based on RiskScore and that cross-sell score.
Q1. As well as project with a class file for the logic calculation, I'm thinking the solution needs another project holding the collection of metrics for a customer.  Does this make sense, or should this class be inside the logic project?
Q2. Before the rules evaluation, this metrics collection will need to be populated, probably using some sp that queries the underlying db. What's the best way to populate the collection? Another web service call? A linq query? Just calling the sp? Response time will be very important.
Q3. The customerId for the query to collect this customer's metrics comes from the web service call; it's an input parameter.  How do I best pass this to the query/service that will get the metrics collection? And then how do I make the results available to the logic project to evaluate the rules and calculate the incentivelevel? As much as possible, I'd like to keep the logic layer separate from the service layer.

A personal note: I'm in New Hampshire, in the northeastern corner of the US.  We've got a lot of snow outside, with another foot or more expected over the next couple of days.  I'm likely to be working from home at all hours. I really appreciate your help on this project. Thanks for the attention.
0
Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

 
LVL 35

Expert Comment

by:Miguel Oz
ID: 40582099
I think this question is growing out of scope very quickly.
Let me clarify  the following items:
A. Even though collection of metrics come from different tables.
Q1. Does GetTopCrossSells web method need to support them all?
Q2. Related to 1. Are these metrics used as a single or separated logic?
For example if the metrics coming from CrossSellScores and CustomerScores tables can be used independently then you need two web methods, one for each table:
   GetTopCrossSells
   GetCustomerScores

B. If you need a separate business logic:
Q3. Did you need the logic in a separate class library?
Business logic could be implemented in the server so does it is consumed by the service layer. In addition you could have an extra UI business layer that behaves more like helper classes that is consumed by your windows or web application.
My preference is to have both.

Ideally your app should have the following layers
1. Presentation layer (UI + UI business classes)
2. Service Layer (Web service)
3. Business logic layer (self explanatory - I will add metric handling here)
4. Data layer (DB)
Let me know if this is the layer mapping you have in mind.

C. Response time.
If response time is important then your Business logic layer ( that is consumed by web service) should handle all metric logic.

Hope it helps,

P.S. I am located in Sydney Australia. It is a weird summer with cold days at the moment.
0
 

Author Comment

by:Mark Klein
ID: 40582324
Yes, helpful. Here's the business situation: We produce  predictive analytics for our clients.  The resulting db of analytics is typically consumed for direct marketing campaigns.  I'm trying to build a real time web service that delivers analytics for a single customer whenever a client app (such as a browser or call center application) raises a request. It's contextual messaging.  There are two inputs, the enduser customerId (which might be anonymous) and a product identifier for the product of interest (for example, what the enduser is looking at on the website or what the enduser has just purchased). There are two outputs: the recommended product(s) to offer and the recommended incentive level (discount). The recommended products are in the analytics db; the recommended incentive level comes from business rules.

I've built a demo client app which consumes the service.  It supplies the customerId and the product of interest. It returns the recommended products.  So far, so good. Now I'm trying to add the recommended incentive level. Typical rules for calculating an incentive are based on customer metrics such as loyalty rank and risk of defection, as well as the likelihood of purchase ("cross-sell score") of the recommended product. These metrics as well as the product recommendations are all in the analytics db.

In your architecture layers, 2-4 are exactly what I am trying to build in the server-based web service. My demo client app is presumably the presentation layer.  Down the road I plan to build a browser app to build the business rules for the logic layer, putting the rules in another db. As of now, I've got some logic in the stored proc, where recommended products need to have a cross-sell score >6. Otherwise, yes, I want the logic layer to handle the metrics. The logic layer has to be on the server and not in client side apps.

I am planning on multiple service requests.  Right now I'm trying to build the GetTopCrossSellScores request.  The SP could be expanded to collect RiskScore, for ex, from the db at the same time that it collects cross-sell score and recommended product. What I'm trying to figure out is where and when to do the incentive level calculation. I'm thinking it should happen in a separate file called by the GetTopCrossSell method in the service1.svc file.
0
 
LVL 35

Expert Comment

by:Miguel Oz
ID: 40582868
I know it is a subtle call because it depends on how big the program is and time available to you; your suggestion is correct , thus you should:
1) Modify the SP to provide RiskScore.
2) Create a static method GetIncentive level with all the required parameters to your required calculation.
3) Use the method in your web service to assign incentive level in the web method.

In that sense your business logic is separate from database and service layer. I know it is more work but it is small price in the long term. Be aware that you may tune up/change the code afterwards as the business logic grows.
Once you finish your app with working functionality, you should start working on performance.  Usual suspect is database interaction.

P.S. Small world. I am working in market research as well see my profile for more details. Our product does retail analysis of sales both historical and predictive retail data.
0
 

Author Comment

by:Mark Klein
ID: 40582938
Hi Miguel--Your experience and qualifications overwhelm me.  My skills are primarily elsewhere.

During the Down Under night (I'd like to go there sometime), I've been trying to implement this approach. First, I extended the TopCrossSells class to include a string IncentiveLevel.
Next, in another project, called ArrowsBusLogic, I built some business logic for a simple rule that sets IncentiveLevel based only on score. See attached file, and don't groan too much.  Remember I'm almost 81 and recovering from a 50+ years gap in programming, probably last did this way before you were born.
IncentiveLevelCalc.cs
0
 

Author Comment

by:Mark Klein
ID: 40582947
Now I'm trying to use this in my service.  Could it be as simple as adding a line like
IncentiveLevel=GetIncentiveLevel (@Score) to this method, in the foreach clause? Just below 'Ranking' in the results section? And what about the uspGetTopCrossSellsResults? Do I need to add something there for the new member?
 public List<TopCrossSells> GetTopCrossSells(string CustomerId, string ProductCategory)
        {
            List<TopCrossSells> results = new List<TopCrossSells>();
            LongbowDemoDataContext dc = new LongbowDemoDataContext();
            int CustomerIdNumber = int.Parse(CustomerId);
            double? Score = 0;
            string ClientKey = "";
            int? Ranking = 1;

            foreach (uspGetTopCrossSellsResult Scores
             in dc.uspGetTopCrossSells(CustomerIdNumber, ProductCategory, ref Score, ref ClientKey, ref Ranking))
            {
                results.Add(new TopCrossSells()
                {
                    Score = (float)Scores.@Score,
                    ClientKey = Scores.@ClientKey,
                    Ranking = Scores.@Ranking
                });
            }
            return results;
        }        

Open in new window

0
 
LVL 35

Accepted Solution

by:
Miguel Oz earned 500 total points
ID: 40585145
Few suggestiions:
1) Make GetIncentiveLevel method static, so you do not need an instance of IncentiveLevelCalc.
public static string GetIncentiveLevel(float Score) 
{//Your code goes here}

Open in new window

2) Use a more generic name for class say MetricsCalc or CrossSellCalc, this name would allow to add more calculation methods in the future.
3) You are correct, you only need to add a line as follows:
IncentiveLevel=IncentiveLevelCalc.GetIncentiveLevel (@Score);

Open in new window

4) Regarding SP, you just need to refresh the generated code with the new SP definition. There should be a refresh context menu that helps you do that or you can delete the SP from design surface and drag it again. It is better to let VS to the upgrade job for you.

Hope it helps,

MS
0
 

Author Comment

by:Mark Klein
ID: 40585204
Miguel, you've been great.  It's been a heavy snow day here in NH, over another foot of snow, and I took advantage of the quiet. I have version running. Some of the pieces:
1.  I simplified the rules calc, put it in a library in ArrowsBusLogic, and made the routine static. Named it GetIncentiveLevel. Took some experimentation to place it correctly
2. I added that line to the service.svc methods, and got it working when I properly qualified the variable @score
3. also had to add the IncentiveLevel property to uspGetTopSellScoresResult and there was some diddling around getting the data types consistent.
It's running, and now I'm working on moving it to our cloud server from my laptop.
Yes, let's talk on Skype Friday night.
0
 

Author Closing Comment

by:Mark Klein
ID: 40585207
Miguel was super, very responsive and knowledgeable and patient with my inexperience.
0
 
LVL 35

Expert Comment

by:Miguel Oz
ID: 40585581
NO worries, glad I could help.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
Help needed on website authentication 7 49
Mutually exclusive checkbox in a gridview 18 42
asp.net mvc5 3 38
Asp.net mvc foreach 3 12
IntroductionWhile developing web applications, a single page might contain many regions and each region might contain many number of controls with the capability to perform  postback. Many times you might need to perform some action on an ASP.NET po…
International Data Corporation (IDC) prognosticates that before the current the year gets over disbursing on IT framework products to be sent in cloud environs will be $37.1B.
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…
Illustrator's Shape Builder tool will let you combine shapes visually and interactively. This video shows the Mac version, but the tool works the same way in Windows. To follow along with this video, you can draw your own shapes or download the file…

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now