Integration NetTiers code with SCSF

Topics: CAB & Smart Client Software Factory
Aug 30, 2006 at 11:36 AM
originally posted by: bil_simser

Hi guys,

I'm trying to figure out the best approach to something. If you're familiar with NetTiers, the latest version will generate your DAL for you and it all works quite well. They provide a solution to bootstrap what they generate, but you can just import the projects into your own solution.

I have my SCSF solution with it's pieces and am trying to integrate the output from NetTiers for my DAL. I've grabbed the Business entity project (which contains empty entities for each table) which is fine and the web project which will give me a web service front end.

The problem is that I'm trying to bridge two things together. I have a WebServicesProxy project where I'm adding my smart web references. I have a service agent for the app that will route through this to access all web services (using a timeout and callback). This is all cool. NetTiers however also generates a front-end for the web services it exposes. They're not as "smart" as the smart web references that SCSF uses but they do use the provider models (via entlib) that get generated.

So I think I have a couple of options:

1. Use the DAL generated and create smart web reference to the web service it creates
2. Use the DAL generated and the web service it creates, but consume it directly

With #2 however I'm finding that I'm going to have to do translations from what the web service creates and my real business objects.

Has anyone done this already and have some suggestions on combining the two frameworks together effectively?

Thanks.
Aug 30, 2006 at 11:47 AM
originally posted by: bil_simser

Just did a little whiteboarding and it's a little confusing (probably along with the note above). here's a more simplified version (I think):

SCSF = Smart Client Software Factory generated/domain
NT = NetTiers generated

Option #1:
SCSF.Client -> NT.WebServiceClient -> NT.WebService -> NT.DAL -> database

Option #2:
SCSF.Client -> SCSF.ServiceAgent -> SCSF.WebServiceProxy -> NT.WebService -> NT.DAL -> database

Option #3:
SCSF.Client -> SCSF.WebServiceProxy -> NT.WebService -> NT.DAL -> database

Yeah, I know. Clear as mud.
Aug 30, 2006 at 12:14 PM
originally posted by: bil_simser

I think I've answered my own question after doing some playing around.

The problem with creating a smart web reference to the generated web services from NetTiers, is that the commands created are going to change. If you add a new table, you'll have a new provider that NetTiers creates and this won't be available in the web service proxy project (or wherever you create your smart web reference). Unfortunately there's no "Update Smart Web Reference" recipie so that won't work.

So really you have to use the generated webserviceclient from nettiers and just call it directly from your service agent or some abstraction (it would be ugly to make a direct reference to it in a module).

In a nutshell here's how I got NT and SCSF to work together:
1. Create a solution folder called NetTiers
2. Create a physical folder to dump the output from CodeSmith into it (somewhere in your source tree)
3. Do a one-time gen of the code to create the projects
4. Add the BusinessLayer, DataAccessLayer, DataAccessLayer.SqlClient, WebServiceClient, and WebService projects to the solution folder

To use it, just add a reference to the WebServiceClient somewhere and make calls to the generated provider classes to pull back data from SQL (via web services). This will give you a nicely multi-tier system that you can update when your schema changes (and there are some nice bonus things the business entities NetTiers creates like validation rules, etc.)

Now the challenge of getting CodeSmith to run automatically to regen the code when the schema changes. That's a whole 'nuther problem as solution files and msbuild don't get along very well for this.

Anyways, I'll leave this info here (and maybe post a blog about it) in case anyone is looking to try to tie the two frameworks together.
Aug 30, 2006 at 4:36 PM
originally posted by: TransParentWebcam

Thanks for the post; I'm attempting the same thing right now. So far, I've created a blank solution and added all the NT and SCSF projects to it; now I have to create a simple smart client to test the NT webservice...
Sep 6, 2006 at 11:11 AM
originally posted by: MichaelDaniel

We've done it by keeping the two completely seperate and by using object translators.
Our NT generated Business, Data and Web Service layers are in their own solution, the SCSF code is in another. The Web Service is automatically published each night as a part of our TFSBuild onto a build server and we simply reference it as a Smart Web Service in the SCSF solution.
If the interface for the Web Service ever changes, we just drop and re-create the Smart Web Reference, tweaking our object translators if required. It's surprisingly fast to regenerate everything and the translators save our front-end developers a lot of work.
Sep 6, 2006 at 12:42 PM
originally posted by: bil_simser

Michael,

How do you deal with the generated business classes for accessing data? For example let's say you have a Customer table which will create the Customer partial classes in the buiness project. Now you want to add business logic that is not setting properties or something (some calculation that's outside of the database). Normally you would modify Customer.cs in the business project as it doesn't get overrwritten with NetTiers. What do you guys do?
Sep 6, 2006 at 5:33 PM
originally posted by: bil_simser

Michael (or anyone using NetTiers with a smart web reference for that matter)

Could you provide info about your setup? I'm using various versions of NetTiers but I can't get a smart web reference to the service to work.

I have NetTiers generate the web service, build it, and it works fine on it's own.

In my SCSF solution I created a project to hold smart web references to but when I create smart web references to the service it complains at compile time that every method doesn't implement the TABLENAMEProvider_xxx method:

Error 1 'WebServiceProxies.localhost.MyServices' does not implement interface member 'WebServiceProxies.localhost.IMyServices.TableNameProvider_Find(string, int, int, int)' C:\dev\Spikes\SCSF-NetTiers\SCSF\SmartClientDevelopmentSolution1\WebServiceProxies\Web References\localhost\Reference.cs 33 26 WebServiceProxies

Any ideas?
Sep 6, 2006 at 9:59 PM
originally posted by: MichaelDaniel

I'm not sure if this answers your question Bil, but check the types that are being exposed by the Net Tiers generated web service.
Your proxy interface may be expecting (for example) an int but is getting an int?
Additionally, we don't actually reference the Net Tier generated business objects. Our Smart Client has it's own object types and we translate the web service objects using Entity Translators.
Our projects are set up as follows:
- We have a seperate Smart Client Solution, and a seperate Business/Data Access Solution. The Web Services also reside in the Business/Data Access solution.
- We use Net Tiers to generate our Data Access and Business Layers.
- The Web Service project is a Web Application project (created using the Web Application Project plugin for Visual Studio 2005)
- The Web Service layer is built nightly using TFSBuild and published on our build server using a Web Deployment project.
- I then reference the Web Service from within the Smart Client by adding a Smart Web Service reference.
- The Smart Web Reference is created by locating the published web service on the build server (eg "http://BuildServer/MyService/Service.asmx")
- To implement the Web Service commands I use code similar to the following:


private void AddServices()
{
MyService.Service service = new MyService.Service();
service.Credentials = System.Net.CredentialCache.DefaultCredentials;
MyService.ServiceProxy proxy = new MyService.ServiceProxy(service);
WorkItem.Services.Add<MyService.IServiceProxy>(proxy);
}

and then make the calls to the web service methods similar to the following:

internal void GetMyData(int param1, string param2)
{
Entities.MySCSFObject myObject = null;
_serviceproxy.GetMyDataById(Properties.Settings.Default.WebServiceProxyTimeout, param1, param2,
delegate(bool success, MyService.MyNTObject result)
{
if (success)
{
myObject = _translator.Translate<Entities.MySCSFObject>(result);
View.ShowMyData(myObject);
}
else
{
View.ShowError("Could not get data");
}
});
}

Note the translation being called for the MyService.MyNTObject to convert it into a Entities.MySCSFObject.
_serviceProxy is of type MyService.IMyServiceProxy.

You may also have to explicitly cast some properties (eg from int? to int, date? to date etc) as a part of the translation between the net tier generated objects and the SC objects.
Hope that helps...
Sep 6, 2006 at 10:02 PM
originally posted by: MichaelDaniel

Sorry, double posted... my connection dropped out while submitting :(
(Who'd have thought in this day and age people would still be using 28.8k dialups to get to the internet from work... thank god it's only temporary)
Sep 7, 2006 at 1:00 AM
originally posted by: bil_simser

Hi Michael,

Yes, that helps a lot. I think I have everything fitting together in the puzzle. I'll check on my web service and figure out what's going on (it's just the service generated by NT so not sure why it would be creating an int?).

You mention that your smart client has it's own object types so I'm assuming that's your real business objects (MySCSFObject) and your translators just map between the NT generated one to your own business entity. So do you double checking on rules and have properties in both that are the same?

For example let's say your MyNTObject has a field in the database called Name with a max length of 50. NT will generate a class called MyNTObject, create a property of type string, and create a validator on the setter of that property to invalidate the object if it's set longer than 50.

So in your SCSFObject do you have a property called Name as well? And do you have it checking the length as well based on a business rule rather than a data rule (e.g. throw an exception if the length is set more than 50).

An option I thought would be to embed the NTObject inside a business entity and call it something like MyObjectData like this:

class MySCSFObject
{
private MyNTObject _data;

public string Name
{
get { return _data.Name; }
set { _data.Name = value; }
}
}

Then you could do something like this for validating business rules:

class MySCSFObject
{
private MyNTObject _data;

public string Name
{
get { return _data.Name; }
set {
_data.Name = value;
if(!_data.IsValid)
throw new ApplicationException(_data.Error);
}
}
}

Of course you're making your business entity aware of the data type and have to pull those references in, but to me it makes sense to leverage the validation framework and pseudo-auto validators that NT already gives you. This way you can modify a field (say a length) and your business rules won't change.

Just wondering how you're using the NT objects in your SCSF ones (if at all).

Thanks!
Sep 7, 2006 at 10:48 AM
originally posted by: MichaelDaniel

You are correct in that we aren't using the NT Objects within the SCSF project at all. The (misguided?) idea being that we are trying to keep our Smart Client as loosly coupled to it's data source as possible. Our Smart Client is not reliant on using just a single source to retrieve it's data.

Additionally, keeping the business objects seperate sped up development as our Smart Client team could make a good start well before the Web Service, Business and Data team had finished. The SC team originally knocked up a 'dummy' web service (within the SC solution) that returned randomly generated values for their business object properties, and as our NT generated layers and Web Services came online it was a relatively easy task to swap them over. The dummy methods also gave the Web Service team a good insight into the methods the smart client team wanted exposed.

It also meant we could demonstrate GUI functionality to the client from a very early stage (using the dummy data we could run demos on a standalone laptop) and helped boost our confidence in the use of the SCSF as we could see things beginning to work (we started development well before the HOL labs came out, so we were using a lot of trial and error and reading of this forum to work out how to best do things at the start)

While most of our objects have a 1:1 mapping for their properties there are quite a few 'extra' properties that the client uses that are specific to the client only. (In a 'real world' situation you would expect a greater divergance between the Web Service properties and the smart client properties as the web service would not be consumed by just your particular smart client app)

Whether this was the best way to go about it or not I'm not too sure, but it meant we did a lot of work in parrallel and quickly, and so far it seems to be working well... touch wood.

The real benefits to date have been whenever a change is made to the NT generated objects and it takes a matter of minutes to get it all working through the client.

Re double checking on properties for business rules: we are lucky in one situation in that most of the data we consume from the web service in our client is read-only (it's primarily metadata on audio).

This all being said: I'm no expert on the subject, and I'm only too happy if someone can point out a better way for us to do things. If your web service and Smart Client are to be tightly coupled then your idea of embedding the NT object in the SC object sounds great ;)
Sep 11, 2006 at 12:41 PM
originally posted by: TransParentWebcam

Bil,

Are you still going down the WebServiceClient reference route, or have you switched over to Michael's translators method?

I'm trying to figure out where to add a reference to the webserviceclient so that it can be shared by all of my modules, and what other edits (adding it to constant's files, adding it as a service....??) I need to do to make it visible to all modules.

A couple other things:
* I couldn't add a smart web ref directly to my NT web service either; got compile errors like you did
* Using the webserviceclient with it's validation should come in handy for offline use - the business validation can take place without a webservice connection - so that would eliminate the need to redo it in the translator, which appeals to me.
Sep 11, 2006 at 1:21 PM
originally posted by: bil_simser

I adjusted my own solution to use the generated code from NetTiers as a reference. It was causing too many problems when it was part of the SCSF solution so I just reference it.

I still can't get a smart web reference to the web services NT generates. I'm using the more current NT templates so they may have changed things around making it difficult to do this. So I'm using the Web Services Client generated from NT (again, just as a reference in the SCSF service agent).

It's pretty clean and works. Anytime the db schema is updated, we just regen the NT code and relink our SCSF solution.
Sep 11, 2006 at 2:15 PM
originally posted by: TransParentWebcam

Did you add the NT references to one module, and then just use them? Or, do you know how to add them to, say, a foundational module or the infrastructure.module project, and then be able to access them from all business modules?

Here's what I'm trying to do:
I have my NT projects generated in their own solution like you suggest. I have a SCSF solution that I want to add three business modules to: Lease, Customer, and Search. Each of these business modules use the NT.WebServiceClient, so I don't want to add the NT references to each module; instead, I wanted to just add them once where every business module could access them.

I am struggling with the SCSF architecture, to put it mildly!! Do you do any moonlighting consulting work? :) I could really use a kick-start to get this project rolling..
Sep 11, 2006 at 2:24 PM
originally posted by: bil_simser

Here's how our solution shapes up:

ServiceAgent - class library that is referenced by any module. Services are created in here and injected into a module via the AddServices method.
WebServiceProxies - class library with smart web references to external web services.

In our case, we linked the ServiceAgent directly to NT via the WebServicesClient (although I guess I could have put the reference in the WebServiceProxy project and manually exposed it).

If I can get NT to work with smart web references, then we'll generate them in the proxy project and reference them in the ServiceAgent. This keeps any NT code out of anything above the ServiceAgent layer so the modules only deal with pure business objects (or DTOs).

The ServiceAgent project contains service classes that will perform the translation either from types coming back from the smart web references, or NT entities.

HTH
Sep 12, 2006 at 7:22 PM
originally posted by: TransParentWebcam

I'm getting more of a handle on it now, but am still a little confused.

I've experimented with the help file on "Translating Between Business Entities and Service Entities", which sounds like what is being recommended from both of you. Bil's "WebServiceProxies" class is akin to the "MyModule.Proxies.cs" class in the "MyModule.ServiceProxies" project, I think, and you are rolling your own smart web references (asynchronous with timeout) in the ServiceAgent class (how?).

But just when I think I understand it...poof...it all goes away. It would be great if you or someone could create a blog post on how to do this in more detail! Maybe I'm confused because Bil and Michael are approaching it slightly differently? I'll try what Michael described next - creating a web service project in my NT solution, then adding a SCSF smart web ref using the recepie, then using the translators to consume it...
Sep 13, 2006 at 12:52 AM
originally posted by: bil_simser

I have a fairly long blog article I've been putting together over the past week so now I just have to drop in some code samples and screenshots to pull it togeher. I'll post the link here when it goes online. It should show how everything fits together (at least for me).

If I could just get the smart web reference to work between SCSF and NT it would be much better, but in the interim I'm using the Web Service Client project that NT generates as my pseudo-proxy. The nice thing is that using the NetTiers provider, I can just swap out SQL for Web Services (the devs have the SQL provider configured and the testers/prod will use the Web Service provider).