Can LLBLGen Pro generate interfaces?

Posts   
 
    
KCarterSr
User
Posts: 15
Joined: 02-Nov-2018
# Posted on: 02-Nov-2018 15:49:54   

I've looked all over the forum and can't find a really direct answer to this question.

Can LLBLGen Pro generate interfaces "automatically" for the entities and dbcontext it generates?

In our current applications this woks great for us. However, with our current tool (DevArt) we manually create the interfaces from the generated entities and dbcontext. We've written T4 templates to do this, but we have many databases and every time we need to add some databases or tables we have to do this again, and again. Like I said, our architecture works great for us, but it's very tedious. Seems like a simple function that should be part of any ORM tool. So were looking around at other tools like yours.

So, can I click a checkbox or something and LLBLGen will do this?

Thanks

P.S.

Oh by the way, I've seen the forum answers that say interfaces aren't necessary. That's another conversation/debate I'd be glad to have in the future. Cheers!

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 02-Nov-2018 16:08:08   

It's not 'built-in' but it's easy to add with a template. I presume this is for entity framework (core) as you mention dbcontext ?

the designer can automatically add interfaces to the generated classes based on macros so it would be fully automatic, the only thing you need to write is a template for the interfaces, which could be created by using the entity poco template used currently for generating the entity classes, so the work would be pretty minimal. The template needed is completely generic so you can use it for all your projects/databases, so it's a one time thing simple_smile .

See: https://www.llblgen.com/Documentation/5.5/Designer/Functionality%20Reference/ProjectSettings.htm#conventions-entity-model-code-generation-attributes--additional-interfaces--additional-namespaces for where to add the definition for the additional interface/s for entities. Using macros you can specify it once at the project level and the interface name is added to each entity class as a type it derives from. Generating the interfaces themselves using a template can then result in 1 file with all the interfaces or one file per interface (one per entity), that's up to you.

Interfaces necessity in the context of ORMs... it's something that doesn't really work as mappings aren't at the interface level. I do agree though with the point that if you want to implement entities with some fields which are the same, it's usable, however you can then also opt for e.g. value types.

In what way do you think interfaces on entities in particular help your software? (For me to understand your use case so we might add default support for it in the future simple_smile ).

If you want us to provide the template for generating interfaces please let us know. Will be next week though before we have it for you (Monday)

Frans Bouma | Lead developer LLBLGen Pro
KCarterSr
User
Posts: 15
Joined: 02-Nov-2018
# Posted on: 02-Nov-2018 19:19:11   

Great, yes please provide the template.

So, I'm going to try to keep this short (sorta).

We use a model based off of MVVM and homebrew dependency injection. Oh, by the way, for our mobile facing applications we use EF Core, .Net Standard 2.0, and Xamarin. We used homebrew dependency injection just to get our developers used to the paradigm of dependency injection at a basic level. We'll be moving to Microsoft Prism after that.

The model is:

MVVM as usual, however I've found that even though the paradigm is considered to be uncoupled between M, V, and VM, that's it's still "forward coupled" . In our model M, V, VM, have no dependencies on each other. Instead they each have a dependency on a dll/project called Inerfaces and Business Logic. It contains all the interfaces that MVVM needs. The interfaces that are generated for the entities and the DbContext are held there along with some business logic. The instances of the interfaces are injected into the MVVM from another module . Let's just call that module the application. Note this is a high level description, so I'll leave it at that. The business logic module also deals with the "IDbContext.

In one of our models is a table called entities (anything that has an address/location).

Heres the interface.

       public interface IEntity
       {
        long EntityID { get; set; }

        long EntityTypeID { get; set; }

        string EntityFullName { get; set; }

        IEntityType IEntityType_EntityTypeID { get; set; } //(person or vehicle or equipment, etc) table

        IList<IEntityAddress>IEntityAddresses_EntityID { get; set; }  // addresses table

}

Here is a snippet of the entity, entity.

    public virtual IList<EntityAddress> EntityAddresses_EntityID
    {
        get
        {
            return this._EntityAddresses_EntityID;
        }
        set
        {
            this._EntityAddresses_EntityID = value;
        }
    }

Here is a snippet of an interface for the customer address.

       public interface IEntityAddress
       {
        long EntityAddressID { get; set; }

        string Street { get; set; }

        long EntityID { get; set; }

        IEntity IEntity_EntityID { get; set; }
       }

    [System.ComponentModel.DataAnnotations.Schema.NotMapped]
    public virtual IList<IEntityAddress> IEntityAddresses_EntityID 
    {
        get
        {
            return this._EntityAddresses_EntityID.ToList<IEntityAddress>();
        }
        set
        {
            this._EntityAddresses_EntityID = value.Select(tableClass => tableClass as EntityAddress).ToList();
        }
    }

Here is a snippet of the DBContext and how it makes the transition.

    public virtual DbSet<EntityAddress> EntityAddresses
    {
        get;
        set;
    }

    public IQueryable<IEntityAddress> IEntityAddresses
    {
        get
        {
         return EntityAddresses;
        }

        set
            {
             EntityAddresses = (DbSet<EntityAddress>)value;
            }
    }

VERY IMPORTANT NOTE: I used Resharper to generate the interface of the DbContext. It takes my DbContext and generates an interface from it. (I hate doing it this way). But, the interface it generates is a "DEEP" generation (I didn't want to go through writing a T4 template to do that) that includes all the public inherited members, of the public inherited members, of the public inherited members, etc..

I hope this helps. I wrote this quickly so sorry about any typo's.

KCarterSr

P.S. I don't like writing T4 templates any more because all the tools for doing that have gone bust except for the T4 plugin for Resharper which is always several months behind Resharper releases. So, I wait (A LOT). If it works out that that I use LLBLGen I'm hoping that your template tool will fill my needs (with intellisense?).

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 05-Nov-2018 05:14:00   

I don't really follow how many templates you want or how you would like generate them (based on certain property of the entity that tell you if it's of type Address, for instance?) Please try to resume the requirement.

Almost anything could be done with templates. Please take a look at the SDK documentation

David Elizondo | LLBLGen Support Team
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 05-Nov-2018 14:17:17   

Yeah I think it's a bit large to provide, so I gave you the template to generate an interface for each entity into a separate csproj file, how to create the files needed so you can get started with that and extend from there if you want.

  • Load your project in the designer
  • Alter project settings -> Additional Templates folder -> Set to .\Templates -> Additional Tasks folder -> set to .\Tasks (these folders have to be created in the folder where the project file is so they're relative to the project file)

    Under Entity model -> Code generation -> Additional interfaces, select 'Entity' and specify as interface name in the grid: I{$Name}. Press TAB to make sure the row is added In 'Additional namespaces', select entity and specify the rootnamespace you're going to use + 'EntityInterfaces'. In my test I used 'AW' for adventureworks, so my additional namespace becomes: AW.EntityInterfaces

  • open template bindings viewer (Tools -> Template bindings viewer), click New

  • Specify as name "InterfacesTemplates"
  • Specify for templates folder the .\Templates folder, (Click open the combo box)
  • For the filename, specify "InterfacesTemplates.templatebindings"
  • For frameworks, platforms and databases, select the ones applicable
  • In the grid, click the top line and specify: TemplateID: EntityInterfaceID Filename: EntityInterface.lpt Logic language: C#

And: TemplateID: CSProjID Filename: CSProj.template Logic language: TDL

Click Save And Close.

The template bindings viewer will now show the new bindings with the two entries in red, to show the template files themselves don't exist.

  • Double-click the EntityInterfaceID line and click 'Yes' when the designer asks you to create it.
  • A blank template editor opens.

Copy / paste the following code into it:


<%
    Project currentProject = _executingGenerator.ProjectDefinition;
    bool emitForeignKeyFields = currentProject.GetRealBoolSettingValue("EmitForeignKeyFields");
    EntityDefinition entity = (EntityDefinition)_activeObject;
    bool isSubType = entity.IsSubType;
    var allRelationshipInfosToTraverse = GeneratorUtils.GetAllRelationshipInfosForEntity(_executingGenerator, entity)
                                                    .Where(ri=>ri.RelationshipType!=EntityRelationshipType.ManyToMany)
                                                    .ToList();
    var entityFields = entity.Fields.Where(f=>!f.IsDiscriminator).OrderBy(f=>f.Name, ApplicationUtils.GetNameOrderingComparer()).ToList();
    if(!emitForeignKeyFields)
    {
        entityFields = entityFields.Where(f=>!f.IsForeignKeyField || (f.IsForeignKeyField && f.IsPartOfIdentifyingFields)).ToList();
    }
    // determine the readonly fields. These fields have to get a backing field. 
    var readOnlyFields = new HashSet<FieldElement>(entityFields.Where(f=>(f.IsReadOnly && currentProject.GetRealBoolSettingValue("GenerateReadOnlyFieldsAsReadOnlyProperties")) || 
                                        f.OutputSettingValues.GetRealBoolSettingValue("GenerateAsReadOnlyProperty", currentProject)));
    _executingGenerator.StoreValueInRunQueueCache(GeneratorConstants.EntityModelEntityClassesNamespaceCacheKey, _executingGenerator.RootNamespaceToUse + ".EntityClasses");
    _executingGenerator.StoreValueInRunQueueCache(GeneratorConstants.EntityModelFkFieldsAreHiddenCacheKey, !emitForeignKeyFields);
%>//------------------------------------------------------------------------------
// <auto-generated>This code was generated by LLBLGen Pro v<%=ApplicationConstants.LLBLGenProVersion%>.</auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
<%  foreach(var additionalNamespace in entity.GetAdditionalNamespaces(currentProject))
    {
%>using <%=additionalNamespace%>;   
<%  }
%>
namespace <%=_executingGenerator.RootNamespaceToUse%>.EntityInterfaces
{
    /// <summary>Interface which represents the entity '<%=entity.FullName%>'.</summary>
    public partial interface I<%=entity.Name%><% if(isSubType) {%>: I<%=entity.GetSuperType().Name%><%}%>
    {
<%  foreach(IFieldElementCore field in entityFields)
    {
%>      /// <summary>Gets<%if(!readOnlyFields.Contains(field)) {%> or sets<%}%> the <%=field.Name%> field. <%=field.Description%></summary>
<%      string dotNetFieldTypeName = SD_EF_GeneralUtils.ProduceDotNetTypeName(field, _executingGenerator, "Nullable<{0}>");
        if(readOnlyFields.Contains(field))
        {
%>       <%=dotNetFieldTypeName%> <%=field.Name%> { get; }
<%      }
        else
        {
%>      <%=dotNetFieldTypeName%> <%=field.Name%> { get; set;}
<%      }
    }

    foreach(var relationshipInfo in allRelationshipInfosToTraverse)
    {
        if(relationshipInfo.NavigatorIsHidden)
        {
            continue;
        }
%>      /// <summary>Represents the navigator which is mapped onto the association '<%=relationshipInfo.Relationship.ToString()%>'</summary>
<%      switch(relationshipInfo.RelationshipType)
        {
            case EntityRelationshipType.ManyToOne:
            case EntityRelationshipType.OneToOne:
%>      I<%=relationshipInfo.RelatedEntity.Name%> <%=relationshipInfo.Navigator%> { get; set;}
<%              break;
            case EntityRelationshipType.OneToMany:
            var collectionType = "IList";
            var settingValue = relationshipInfo.NavigatorInstance.OutputSettingValues.GetRealStringSettingValue("CollectionNavigatorType", currentProject);
            switch(settingValue)
            {
                case "List<T>":
                    collectionType = "IList";
                    break;
                case "ICollection<T>":
                    collectionType = "ICollection";
                    break;
                case "IEnumerable<T>":
                    collectionType = "IEnumerable";
                    break;
            }
%>      <%=collectionType%><I<%=relationshipInfo.RelatedEntity.Name%>> <%=relationshipInfo.Navigator%> { get; set;}
<%              break;
        }
    }
    
    foreach(var forf in entity.FieldsMappedOntoRelatedFields.OrderBy(f=>f.Name, ApplicationUtils.GetNameOrderingComparer()))
    {
        if(forf.MappedField.Field.IsForeignKeyField && !emitForeignKeyFields)
        {
            continue;
        }
        string typeName = SD_EF_GeneralUtils.ProduceDotNetTypeName(forf.MappedField.Field, _executingGenerator, "Nullable<{0}>");
%>      /// <summary>Represents the related field 'this.<%=forf.MappedFieldAsString%>'</summary>
        <%=typeName%> <%=forf.Name%> { get; <% if(!forf.IsReadOnly) {%> set; <%}%> }
<%  }
%>  }
}

  • Press cntrl-shift S to save the text file.

  • double click the CSProjID line, again click 'Yes' to create the template file

  • copy / paste the following code into it:

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>netstandard2.0</TargetFramework>
        <AssemblyName><[ProjectName]>.Interfaces</AssemblyName>
        <RootNamespace><[RootNamespace]></RootNamespace>
    </PropertyGroup>
    <ItemGroup>
<# SD_DbSpecificPackageReferenceIncludeTemplate #>  
    </ItemGroup>
</Project>  

  • open the Presets viewer (Tools -> Presets viewer)
  • Under found presets select the one you want to use. If you're targeting .net core as you do, select the netstandard one then click 'Copy as new' on the right.
  • For the Tasks folder in the dialog that pops up, select the .\tasks folder at the bottom of the combo box.
  • The preset opens in the xml editor.

  • The interfaces have to be generated in a separate csproj, so they can be referenced without the entities.

  • Under the <taskPresets> node, copy/paste the following xml. It generates all code for the interfaces into a new csproj.


<taskGroupPreset name="SD.Tasks.Base.GenericTaskGroup" displayName="Generate Interfaces" requiresCleanCache="false">
  <taskPreset name="SD.Tasks.Base.ConsumeLptTemplate" displayName="Generate Entity Interfaces" requiresCleanCache="false">
    <parameters>
      <parameter name="destinationFolder" value="Interfaces\EntityInterfaces" />
      <parameter name="templateID" value="EntityInterfaceID" />
      <parameter name="emitType" value="allEntities" />
      <parameter name="templateAssemblySourceFileFormat" value="" />
      <parameter name="filenameFormat" value="I[elementName].[extension]"/>
    </parameters>
  </taskPreset>
  <taskPreset name="SD.Tasks.Base.GenerateVSNetProject" displayName="Generate VS.NET project file" requiresCleanCache="false">
    <parameters>
      <parameter name="cacheFilenameWithID" value="3" />
      <parameter name="destinationFolder" value="Interfaces" />
      <parameter name="templateID" value="CSProjID" />
      <parameter name="buildActionPerExtension" value=".config;None|;Compile" />
      <parameter name="filenameFormat" value="[projectName].Interfaces.[extension]proj" />
    </parameters>
  </taskPreset>
</taskGroupPreset>

  • At the bottom of the file, change
<parameter name="referenceProjectsCachedWithIDs" value="1" />

into:

<parameter name="referenceProjectsCachedWithIDs" value="1,3" />

You can ignore this if you want, the designer won't overwrite existing csproj files so you can also manually set the references inside vs and they'll be left untouched. You have to do this anyway for the model project, to reference the interfaces project.

Save the file by pressing ctrl-shift-s and generate code by pressing F7. In the dialog, double click the task to edit it, and select .netstandard as the target platform and select your preset you just created as the preset to use. Specify the destination folder where the code has to be generated.

This will generate a model, a persistence project and an interfaces project. The model requires additional code, as the interfaces define interfaces on the navigators and the model doesn't implement these.

To do that, you have to generate a partial class for each entity, and you can do that in the model project. To do that, first define the template like with the interfaces template, in the templatebindings file. Next, you have to specify the partial class code in the template. then you have to add a task to the preset, inside the <taskGroupPreset name="SD.Tasks.Base.GenericTaskGroup" displayName="Generate Entity Framework Domain model with POCO entities" requiresCleanCache="false"> taskGroupPreset node, so it gets included in that csproj. Just copy the taskpreset for the entity classes, specify the ID you gave to the partial class template and specify a different filename, e.g. it should look something like this:


  <taskPreset name="SD.Tasks.Base.ConsumeLptTemplate" displayName="Generate Entity Classes" requiresCleanCache="false">
    <parameters>
      <parameter name="destinationFolder" value="Model\EntityClasses" />
      <parameter name="templateID" value="EntityInterfaceImpl" />
      <parameter name="emitType" value="allEntities" />
      <parameter name="templateAssemblySourceFileFormat" value="" />
      <parameter name="filenameFormat" value="[elementName]InterfaceImpl.[extension]"/>
    </parameters>
  </taskPreset>

This should get you started with the code generator, how things work. The template code is basically similar to T4 except you don't have to do a lot of the work. To see how the object model looks like at runtime, please right-click the project node in the project explorer -> Run plugin -> Project explorer.

You can query this model inside the designer using a Linq query in C# by using Element search. This could help you write your templates.

Relevant docs: SDK docs: https://www.llblgen.com/Documentation/5.5/SDK/index.htm and https://www.llblgen.com/Documentation/5.5/Designer/How%20Toworkwithtemplates.htm

We also support derived models which are models projected from your entity model and which are very useful in MVC/MVVM scenarios, they support denormalization if you want and come with code to update entities in the DB if you need to, as well as projection code to instantiate them from a linq query. The models themselves have no coupling with the entity model nor with the persistence code. the persistence code has coupling with the dbcontext of course.

I must say that I don't really see why you'd go the interface route considering the entity model classes (the pocos) are already decoupled from everything (they have no coupling with anything so why create an extra abstraction layer?). The additional downside of the interfaces is that you create a separate hierarchy of types which is parallel to the entity hierarchy (with navigators) and you can't cast one to the other (Customer.Orders contains Order instances. ICustomer.Orders contains IOrder instances, not Order instances. You might think you can cast them to Order but technically it don't have to be Order instances.

Anyway, hope this helps simple_smile

Frans Bouma | Lead developer LLBLGen Pro
KCarterSr
User
Posts: 15
Joined: 02-Nov-2018
# Posted on: 05-Nov-2018 15:57:29   

Thanks for all the info. I'll start with that and work through it.

One thing though. This might be a big ask, but I've already started testing LLBLGen out and have a request. One of the Dialog boxes "Configure the code generation specifics for the task" that appears after selecting "Generate Code". Could you put text box that allows you to enter the name for the .csproj file?

Since it's currently by default, using the namespace for the file name I'm hitting the Windows path length limitation causing the generation to fail. Our file structure along with our namespaces structure prevents us from using the default.

E.G. a couple of our namespaces,

CWMatthews.ActiveDirectoryManager.CrossPlatformNetStandard.Models.EntityFrameworkBased.SQLServer.ServerStatus

CWMatthews.HaulManager.CrossPlatformNetStandard.Models.EntityFrameworkBased.Oracle.Tickets

Thanks K. L. Carter Sr.

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 05-Nov-2018 20:01:56   

You can copy the Preset and edit it to add <parameter name="useRootNameSpaceForProjectName" value="false" /> under the SD.Tasks.Base.GenerateVSNetProject parameters.

Then set the ProjectName in the Project Settings, which will take part in the file naming instead of the rootnamespace.

KCarterSr
User
Posts: 15
Joined: 02-Nov-2018
# Posted on: 05-Nov-2018 20:57:33   

Excellent.

Thank you, I'll do that.

K. Carter Sr.

KCarterSr
User
Posts: 15
Joined: 02-Nov-2018
# Posted on: 06-Nov-2018 03:01:55   

FYI, Couldn't load the project afterwards. Kept throwing an Exception.

I finally got it to work by also adding, isOptional="false"

K. Carter Sr.

KCarterSr
User
Posts: 15
Joined: 02-Nov-2018
# Posted on: 06-Nov-2018 03:05:55   

Could not complete this step. Specify for templates folder the .\Templates folder, (Click open the combo box).

The only choices were C:\Program Files (x86)\Solutions Design\LLBLGen Pro v5.5\Frameworks sub-directories and the D:\Documents\LLBLGen Pro\v5.5\Templates directory.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 06-Nov-2018 08:54:27   

You have to set the additional templates and additional tasks folder in the project settings, it's the first step in the list of steps I mentioned. So right-click project node in project explorer (or do Project -> Settings in the main menu) -> Settings. the dialog that opens, specify the additional tasks folder and additional templates folder on the right.

If you did that, you'll see a folder in the combo box representing that folder, i.e. <project file location>\Templates.

You can also pick D:\Documents\LLBLGen Pro\v5.5\Templates, but storing it close to the project file is better as it's easier to commit all to source control in 1 go.

KCarterSr wrote:

FYI, Couldn't load the project afterwards. Kept throwing an Exception.

I finally got it to work by also adding, isOptional="false"

K. Carter Sr.

You edited a preset and you couldn't load the project afterwards? That's odd. Could you please give the exception or what you did exactly so we can look into it?

Frans Bouma | Lead developer LLBLGen Pro
KCarterSr
User
Posts: 15
Joined: 02-Nov-2018
# Posted on: 06-Nov-2018 19:08:56   
  1. Okay, when I used <parameter name="useRootNameSpaceForProjectName" value="false" isOptional="false"/> everything went fine.

When I used <parameter name="useRootNameSpaceForProjectName" value="false"/> the following exception is thrown when try to load any project.

Exception information.

LLBLGen Pro version: v5.5. Build: 5.5.0

Exception details:

Message: The file 'C:\Program Files (x86)\Solutions Design\LLBLGen Pro v5.5\Frameworks\Shared\Tasks\SD.Tasks.Base.tasks' couldn't be loaded due to an error: 'The string '' is not a valid Boolean value.' Source: SD.LLBLGen.Pro.ApplicationCore Stack trace: at SD.LLBLGen.Pro.ApplicationCore.CodeGenerationMetaData.Tasks.TaskManager.LoadTasksFiles(String[] additionalTasksFolders) at SD.LLBLGen.Pro.ApplicationCore.Configuration.CoreState.RefreshCodeGenerationMetaData(String additionalTasksFolderPreferences, String additionalTasksFolderProject, String additionalTemplatesFolderPreferences, String additionalTemplatesFolderProject) at SD.LLBLGen.Pro.ApplicationCore.Configuration.CoreState.RefreshCodeGenerationMetaData(UserConfiguration preferences, Project project) at SD.LLBLGen.Pro.Gui.Classes.GuiController.PerformRefreshCodeGenerationMetaData() at SD.LLBLGen.Pro.Gui.Classes.GuiController.PerformOpenProjectAction(String filenameToOpen)

Inner exception:-----------------------

Exception details:

Message: The string '' is not a valid Boolean value. Source: System.Xml Stack trace: at System.Xml.XmlConvert.ToBoolean(String s) at SD.LLBLGen.Pro.ApplicationCore.CodeGenerationMetaData.Tasks.TaskManager.ParseParameters(Task newTask, XmlNode nodeToParse) at SD.LLBLGen.Pro.ApplicationCore.CodeGenerationMetaData.Tasks.TaskManager.ParseTaskNode(XmlNode nodeToParse) at SD.LLBLGen.Pro.ApplicationCore.CodeGenerationMetaData.Tasks.TaskManager.ParseTaskGroupNode(XmlNode nodeToParse) at SD.LLBLGen.Pro.ApplicationCore.CodeGenerationMetaData.Tasks.TaskManager.LoadTasksFiles(String[] additionalTasksFolders)

Inner exception: <null>

2. Yes, your'e were right. I created the folders but didn't set the additional templates and additional tasks folder in the project settings. So, it's working now.

  1. I got all the steps completed and generated the code. It works. But, now I've need to go through all the steps and understand just what everything did/is doing so that that I can have a clear understanding of LLBLGen, the code you supplied, api, etc...

I've got my hands full now! I'll need to get a full grasp of all this before the trial expires! I'll definitely be busy!

  1. I did all of this with a mobile application project and it seems to be working, so that brings me to another IMPORTANT question. I'll create a new topic thread since it's off- topic. Look for that one.

Thanks a lot. I'll get started.

K. L. Carter Sr.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 07-Nov-2018 10:17:10   

KCarterSr wrote:

  1. Okay, when I used <parameter name="useRootNameSpaceForProjectName" value="false" isOptional="false"/> everything went fine.

When I used <parameter name="useRootNameSpaceForProjectName" value="false"/> the following exception is thrown when try to load any project.

Exception information.

LLBLGen Pro version: v5.5. Build: 5.5.0

Exception details:

Message: The file 'C:\Program Files (x86)\Solutions Design\LLBLGen Pro v5.5\Frameworks\Shared\Tasks\SD.Tasks.Base.tasks' couldn't be loaded due to an error: 'The string '' is not a valid Boolean value.' Source: SD.LLBLGen.Pro.ApplicationCore Stack trace: at SD.LLBLGen.Pro.ApplicationCore.CodeGenerationMetaData.Tasks.TaskManager.LoadTasksFiles(String[] additionalTasksFolders) at SD.LLBLGen.Pro.ApplicationCore.Configuration.CoreState.RefreshCodeGenerationMetaData(String additionalTasksFolderPreferences, String additionalTasksFolderProject, String additionalTemplatesFolderPreferences, String additionalTemplatesFolderProject) at SD.LLBLGen.Pro.ApplicationCore.Configuration.CoreState.RefreshCodeGenerationMetaData(UserConfiguration preferences, Project project) at SD.LLBLGen.Pro.Gui.Classes.GuiController.PerformRefreshCodeGenerationMetaData() at SD.LLBLGen.Pro.Gui.Classes.GuiController.PerformOpenProjectAction(String filenameToOpen)

Inner exception:-----------------------

Exception details:

Message: The string '' is not a valid Boolean value. Source: System.Xml Stack trace: at System.Xml.XmlConvert.ToBoolean(String s) at SD.LLBLGen.Pro.ApplicationCore.CodeGenerationMetaData.Tasks.TaskManager.ParseParameters(Task newTask, XmlNode nodeToParse) at SD.LLBLGen.Pro.ApplicationCore.CodeGenerationMetaData.Tasks.TaskManager.ParseTaskNode(XmlNode nodeToParse) at SD.LLBLGen.Pro.ApplicationCore.CodeGenerationMetaData.Tasks.TaskManager.ParseTaskGroupNode(XmlNode nodeToParse) at SD.LLBLGen.Pro.ApplicationCore.CodeGenerationMetaData.Tasks.TaskManager.LoadTasksFiles(String[] additionalTasksFolders)

Inner exception: <null>

Thanks for that. This is indeed a bit harsh: a load error in a related element needed for code generation should be reported as a warning in the list of warnings, not prevent a project to load. It's not something one runs into every day but it should be less strict. We'll look into changing this in the future.

  1. I got all the steps completed and generated the code. It works. But, now I've need to go through all the steps and understand just what everything did/is doing so that that I can have a clear understanding of LLBLGen, the code you supplied, api, etc...

I've got my hands full now! I'll need to get a full grasp of all this before the trial expires! I'll definitely be busy!

You'll get the hang of it simple_smile It's pretty straight forward: - the project is an object model you can freely use in a template. Entity model elements, mappings and meta data are all available to you, there are helper methods available to make this easier - Every template is executed by a task which in turn is performed by a 'task performer' which is a .net class. Which tasks are run and with which parameters is controlled by the preset you're using during code generation. So if you choose preset A with presets for 10 tasks, then 10 tasks are executed using the task performer associated with them. - which template is executed is controlled by templatebindings: a preset/task works with a templateID and at runtime the template associated with that ID is used. This means that if you want to override a template in the shipped set you can simply bind another template to the ID and it will be picked up. - This means that once you have 1-2 templates setup, adding another one means: adding a template-templateID binding, write the template, add a preset for a task to your preset file to run the template with the give ID and it will run.

In general you don't need to generate a lot of extra code most of the time. Your setup is rather complex so you need more customization than usual. I also think that in your case it might be a better choice to use 'derived models' which are a core part of the designer/projects of our system and which don't require any customization of templates. But that might require more customization of your own code if you already have a big system in place.

Frans Bouma | Lead developer LLBLGen Pro
KCarterSr
User
Posts: 15
Joined: 02-Nov-2018
# Posted on: 07-Nov-2018 17:28:23   

In this step you listed "Under Entity model -> Code generation -> Additional interfaces, select 'Entity' and specify as interface name in the grid: I{$Name}. Press TAB to make sure the row is added"

Do need to manually do this for each entity class?

I'm assuming that I have to create a rule to tell LLBLGen what entity class to apply each interface to, in "Under Entity model -> Code generation -> Additional interfaces (Rule).

OR,

If, "I don't use" "Under Entity model -> Code generation -> Additional interfaces

and want the interface name to automatically be I + "entity class name", Do I modify/copy the default template code that generates the entity classes? Where?

Thanks

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 08-Nov-2018 09:36:19   

KCarterSr wrote:

In this step you listed "Under Entity model -> Code generation -> Additional interfaces, select 'Entity' and specify as interface name in the grid: I{$Name}. Press TAB to make sure the row is added"

Do need to manually do this for each entity class?

No, you have to literally specify it as I{$Name}, so the designer will expand it for every entity as I_the name of the entity_, e.g. for Customer it will add ICustomer.

That's what the macros do. The location 'Entity Model -> Code generation -> Additional interfaces' is for all entities, so you specify it there once and it's applied to all entities by default if there's no rule defined. If you want to apply the specified interface on some entities, you can add a rule (e.g. only entities in a certain group) and make the designer enforce the interface only on the entities matching the rule.

The '{$Name}' is a macro, there are more, see: https://www.llblgen.com/Documentation/5.5/Designer/Functionality%20Reference/ElementDefinitionMacros.htm

I'm assuming that I have to create a rule to tell LLBLGen what entity class to apply each interface to, in "Under Entity model -> Code generation -> Additional interfaces (Rule).

OR,

If, "I don't use" "Under Entity model -> Code generation -> Additional interfaces

and want the interface name to automatically be I + "entity class name", Do I modify/copy the default template code that generates the entity classes? Where?

Thanks

Like I said simple_smile Just specify literally I{$Name} and it will be expanded at generation time by the designer as I<entityname> for every entity matching the rule specified (if no rule is specified, all entities match, thus it's applied to all entities). So no work needed simple_smile

If you e.g. want to add the interface INotifyPropertyChanged to all entities, you can specify it there literally as that too. It doesn't contain a macro so it's used as-is. If you include a macro, e.g. IMy{$FullName}, it will expand {$FullName} to a string build by the GroupName+EntityName for every entity and append it to IMy, so an entity in the group Crm called Customer will then get an interface IMyCrmCustomer. The entity Order in the group Sales will then get the interface IMySalesOrder, all from that same single interface definition.

Frans Bouma | Lead developer LLBLGen Pro