Can't get Debug to work at all

Posts   
 
    
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 20-Jun-2008 07:32:04   

I have created an LPT template which will be used as an include from a TDL template. This is working in as much as it emits a comment into the generated code but I need to debug the rest of it.

I've read the forums and found that included LPT templates are a problem in that there is nowhere to specify debug mode for them. I also read that the first LPT usage defines whether debug mode is in effect for all LPT file.

So I created an EmptyTemplate.lpt file along with a binding called EmptyTemplate. I added a ConsumeLptTemplate task with templateID=EmptyTemplate and debugBuild=true and placed this at the top of the DatabaseGeneric task group and saved this as a new preset. (The preset is basically Adapter.General2005).

The breakpoint gets hit and I open a new instance of VS2008 and see it is at __ScriptCode() but there is no debug information available in the stack frame. When I look at the Application Output, it claims that "Could not find template 'EmptyTemplate'. It is not defined in the used templatebinding or is empty. It can be it's not defined for the current database (e.g. stored procedure templates for for SqlServer CE).

I can't work out what that last error could mean since there are four possible problems: 1) Not found - well the file is there and does emit plus the TemplateBindings viewer see it and allows it to be opened. 2) Is Empty - Was originally an empty file so I added "<~ // Ignore this ~>" to it to be sure. 3) Not defined in the used templatebinding. Not sure what the used bit is but it is definitely in my custom template bindings and is at the top of the priority list. 4) Not defined for the current database - no idea if this is relevant since it is all C#

Any ideas what to try next?

Also, whilst playing with this I created a participating object subset with just one TypedList in it. It seems that unless you specifically click on the Create participating object subset button and then click OK (my small set is already selected in the dropdown) every time before generating, it runs for all items. Is this by design or should it be using the selected item in the Project Element Group dropdown?

Cheers Simon

Walaa avatar
Walaa
Support Team
Posts: 14987
Joined: 21-Aug-2005
# Posted on: 20-Jun-2008 11:05:20   

Did you set the templateAssemblySourceFileFormat?

SDK wrote:

  • Create a preset which contains the same tasks as the preset you want to use, however the first DotNetTemplateEngine using task definition has to have the following parameters to make the DotNetTemplateEngine task performer build a debug build of the template code: (see the DotNetTemplateEngine task performer documentation for details about these parameters)
  • debugBuild with the value true
  • templateAssemblySourceFileFormat with the value templateClassesSource.[extension] (or other filename, at least it has to have a value which resolves to a correct filename. This file will contain the sourcecode of the templates)

  • Add the namespace reference <[ System.Diagnostics ]> to one of the templates in your templateset.

  • At the spot where you want to break the execution and you want to step into the debugger, add the following statement (depending on the language you're using for the template logic: VB.NET or C#): <% // For C# Debugger.Break(); %> <% ' For VB.NET Debugger.Break() %>

  • Generate code using the preset which contains the debug build task you defined at step 1. When the Debugger.Break() statement is reached, you'll get a popup which will let you select the Visual Studio instance to debug with. This Visual Studio instance will load the template source code and will position the current statement locator at the Debugger.Break() statement, which allows you to step through the template and examine local variables.

simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 20-Jun-2008 11:54:51   

Walaa wrote:

Did you set the templateAssemblySourceFileFormat?

SDK wrote:

  • Create a preset which contains the same tasks as the preset you want to use, however the first DotNetTemplateEngine using task definition has to have the following parameters to make the DotNetTemplateEngine task performer build a debug build of the template code: (see the DotNetTemplateEngine task performer documentation for details about these parameters)
  • debugBuild with the value true
  • templateAssemblySourceFileFormat with the value templateClassesSource.[extension] (or other filename, at least it has to have a value which resolves to a correct filename. This file will contain the sourcecode of the templates)

  • Add the namespace reference <[ System.Diagnostics ]> to one of the templates in your templateset.

  • At the spot where you want to break the execution and you want to step into the debugger, add the following statement (depending on the language you're using for the template logic: VB.NET or C#): <% // For C# Debugger.Break(); %> <% ' For VB.NET Debugger.Break() %>

  • Generate code using the preset which contains the debug build task you defined at step 1. When the Debugger.Break() statement is reached, you'll get a popup which will let you select the Visual Studio instance to debug with. This Visual Studio instance will load the template source code and will position the current statement locator at the Debugger.Break() statement, which allows you to step through the template and examine local variables.

templateAssemblySourceFileFormat is set to "templatesSource.[extension] and a file called "C:\temp\GeneratorTemp\templatesSource.cs" is created so that seems OK.

I have System.Diagnostics.Debugger.Break() rather than adding a namespace reference.

There is only one Task using the LptParser.DotNetTemplatEngine, and it is the one I created - I have it as the very first item in the Run Queue. Settings are:- destinationFolder=C:\ filenameFormat=Empty.txt templateID=EmptyTemplate emitType=generic failWhenExistent=false templateBindingDefinitionName=BPOSS.templatebindings compileOnly=false debugBuild=true templateIsOptional=false templateAssemblySourceFileFormat=templatesSource.[extension]

The problem is this message which indicate EmptyTemplate is not being used but the question is why not?

---- Running preset TestTypedListDebug

TestTypedListDebug::Executing task group. SD.Tasks.Adapter.DatabaseGeneric::Executing task group. SD.Tasks.Base.ConsumeLptTemplate::Executing task. SD.Tasks.Base.ConsumeLptTemplate::Using class 'SD.LLBLGen.Pro.LptParser.DotNetTemplateEngine' in assembly 'SD.LLBLGen.Pro.LptParser.dll'. DotNetTemplateEngine::Could not find template 'EmptyTemplate'. It is not defined in the used templatebindings or is empty. It can be it's not defined for the current database (e.g. stored procedure templates for SqlServer CE) SD.Tasks.Base.ConsumeLptTemplate::Execution task done.

As I said there are 4 possible reasons for this message and none of them seem to apply.

Cheers Simon

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39773
Joined: 17-Aug-2003
# Posted on: 20-Jun-2008 17:19:40   

It apparently can't find the template, and why not is indeed weird, so if you could attach the preset, templatebindings and the template we can have a look. (you can copy the templatebindings and just strip out everything except that custom one, so you don't have to post sensitive info)

if(executingGenerator.GetTemplateFileContents(templateID, templateBindingDefinitionName).Length<=0)

results in an empty string being returned, which either means the templateID wasn't found or the templatebindingsdefinition name wasn't found. If you dont set templateBindingDefinitionName, it will look for the templateID in all the bindings.

Frans Bouma | Lead developer LLBLGen Pro
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 22-Jun-2008 15:43:19   

Otis wrote:

It apparently can't find the template, and why not is indeed weird, so if you could attach the preset, templatebindings and the template we can have a look. (you can copy the templatebindings and just strip out everything except that custom one, so you don't have to post sensitive info)

if(executingGenerator.GetTemplateFileContents(templateID, templateBindingDefinitionName).Length<=0)

results in an empty string being returned, which either means the templateID wasn't found or the templatebindingsdefinition name wasn't found. If you dont set templateBindingDefinitionName, it will look for the templateID in all the bindings.

I tried without templateBindingDefinitionName initially - adding it was just desparation as was values for destinationFolder and filenameFormat

I have attached all the requested files:

From C:\Perforce\BPOSS\src3\LLBLGen.Pro.Custom\Tasks: TestTypedListDebuge.preset

From C:\Perforce\BPOSS\src3\LLBLGen.Pro.Custom\Templates: BPOSS.templatebindings EmptyTemplate.lpt typedListAdapter.addition.template (the one that includes the lpt file) TypedListAutoSetPrimaryKeyTemplate.lpt (the one I want to debug eventually)

Cheers Simon

Attachments
Filename File size Added on Approval
LLDebugTemplatesFiles.zip 2,551 22-Jun-2008 15:43.32 Approved
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39773
Joined: 17-Aug-2003
# Posted on: 23-Jun-2008 13:16:58   

If I remove templateBindingDefinitionName=BPOSS.templatebindings it will generate Empty.txt. It still whines about that it can't attach to process.

The name is case sensitive, your bindings are called: BPOSS.templateBindings.

I ran a debug build of the interpreter and I saw it re-compiles the lpt templates into another assembly, in-memory and not debug build, if it can't find an assembly in the filecache under a different name as the lpt engine stores it under. The reason this was done is to avoid ruining a lpt template debug situation in a task below a TDL task which included an lpt template.

If I make the codeEmitter and Interpreter use the same key for the file cache, it will re-use the assembly and will thus allow you to attach to process and step through the code. Another way to enable this is to change the line 835 in DotNetTemplateEngine.cs, where the call to CompileTemplates is made (in-memory compile, no debug build).

Though that gives more problems as some things aren't available.

So the best thing you can do, is to add the assembly to the file cache again as "IncludeCompiledAssembly" in DotNetTemplateEngine.cs, line 459 (so keep that line, add a similar line) and use that DotNetTemplateEngine instance as a debug task performer. (and use the normal one during production code generation).

Frans Bouma | Lead developer LLBLGen Pro
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 23-Jun-2008 15:14:17   

Otis wrote:

If I remove templateBindingDefinitionName=BPOSS.templatebindings it will generate Empty.txt. It still whines about that it can't attach to process.

The name is case sensitive, your bindings are called: BPOSS.templateBindings.

I ran a debug build of the interpreter and I saw it re-compiles the lpt templates into another assembly, in-memory and not debug build, if it can't find an assembly in the filecache under a different name as the lpt engine stores it under. The reason this was done is to avoid ruining a lpt template debug situation in a task below a TDL task which included an lpt template.

If I make the codeEmitter and Interpreter use the same key for the file cache, it will re-use the assembly and will thus allow you to attach to process and step through the code. Another way to enable this is to change the line 835 in DotNetTemplateEngine.cs, where the call to CompileTemplates is made (in-memory compile, no debug build).

Though that gives more problems as some things aren't available.

So the best thing you can do, is to add the assembly to the file cache again as "IncludeCompiledAssembly" in DotNetTemplateEngine.cs, line 459 (so keep that line, add a similar line) and use that DotNetTemplateEngine instance as a debug task performer. (and use the normal one during production code generation).

Eh? smile

Does that mean there is a bug and will be fixed in a later build? Or I'm doing something strange and this is a workaround?

Cheers Simon

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39773
Joined: 17-Aug-2003
# Posted on: 23-Jun-2008 16:07:37   

simmotech wrote:

Otis wrote:

If I remove templateBindingDefinitionName=BPOSS.templatebindings it will generate Empty.txt. It still whines about that it can't attach to process.

The name is case sensitive, your bindings are called: BPOSS.templateBindings.

I ran a debug build of the interpreter and I saw it re-compiles the lpt templates into another assembly, in-memory and not debug build, if it can't find an assembly in the filecache under a different name as the lpt engine stores it under. The reason this was done is to avoid ruining a lpt template debug situation in a task below a TDL task which included an lpt template.

If I make the codeEmitter and Interpreter use the same key for the file cache, it will re-use the assembly and will thus allow you to attach to process and step through the code. Another way to enable this is to change the line 835 in DotNetTemplateEngine.cs, where the call to CompileTemplates is made (in-memory compile, no debug build).

Though that gives more problems as some things aren't available.

So the best thing you can do, is to add the assembly to the file cache again as "IncludeCompiledAssembly" in DotNetTemplateEngine.cs, line 459 (so keep that line, add a similar line) and use that DotNetTemplateEngine instance as a debug task performer. (and use the normal one during production code generation).

Eh? smile

Does that mean there is a bug and will be fixed in a later build? Or I'm doing something strange and this is a workaround?

Cheers Simon

It's something that's not really fixable as in: of course it can be changed and your situation will work, however other situations then will run into problems. Bottom line: it's not part of the design that include templates are compiled using debug builds. Would that have been the case, it wouldn't have been a problem.

So you're not doing anything strange, the system just isn't prepared for it. To work around this, add the extra line which adds the compiled assembly also under the other key to the file cache simple_smile

Frans Bouma | Lead developer LLBLGen Pro
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 25-Jun-2008 15:03:54   

OK, I can have a go at that but before I do can I ask about my original problem just in case it can already be done with TDL Templates.

What I need to do for TypedLists is check to see if the first field is a PrimaryKey or not and if it is then set the PrimaryKey on the DataTable in OnInitialized().

I can see that <[FieldIndex]> could be checked for 0 to find the first field but it doesn seem to be available in any of the <[If]> statements or am I missing something?

Cheers Simon

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39773
Joined: 17-Aug-2003
# Posted on: 25-Jun-2008 15:50:15   

Why do you need to check the first field only?

Frans Bouma | Lead developer LLBLGen Pro
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 25-Jun-2008 16:14:05   

Otis wrote:

Why do you need to check the first field only?

Our tables don't use multi-field PrimaryKeys at all but because the TypedList may contain EntityFields from different Entities there may be multiple PKs in the TypedList fields.

We are using these specifically for Lookups and so we can just ensure that the correct PK is the first column and it will be automatically set on the DataTable for quicker lookups.

Cheers Simon

PS Got my template working in the end - I had missed the bit in the docs about included LPT templates always returning a Hashtable - no wonder my safe-casting statements always returned null!

PPS Still confused about the "Create participating object subset..." button though. If you use Generate/Re-run Last Ran Preset then the subset is remembered no problem. If you use F7 then you have to go into "Create participating object subset..." and click OK to use the subset even if there is only DefaultParticipatingObjectsGroup in there, otherwise, it runs against everything.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39773
Joined: 17-Aug-2003
# Posted on: 25-Jun-2008 17:20:09   

simmotech wrote:

Otis wrote:

Why do you need to check the first field only?

Our tables don't use multi-field PrimaryKeys at all but because the TypedList may contain EntityFields from different Entities there may be multiple PKs in the TypedList fields.

We are using these specifically for Lookups and so we can just ensure that the correct PK is the first column and it will be automatically set on the DataTable for quicker lookups.

Ah ok simple_smile

PS Got my template working in the end - I had missed the bit in the docs about included LPT templates always returning a Hashtable - no wonder my safe-casting statements always returned null!

Yes, that's a bit burried indeed... include templates tend to be a bit different when calling, you get the state of the interpreter passed in simple_smile

PPS Still confused about the "Create participating object subset..." button though. If you use Generate/Re-run Last Ran Preset then the subset is remembered no problem. If you use F7 then you have to go into "Create participating object subset..." and click OK to use the subset even if there is only DefaultParticipatingObjectsGroup in there, otherwise, it runs against everything.

You can also create these sets without pressing F7 simple_smile Project -> Manage object groups There you can manage the object groups you'd like to use. The thing with managing them when pressing F7 is that when you click CANCEL, the groups aren't persisted.

Frans Bouma | Lead developer LLBLGen Pro