I have a project containing several interfaces and I want to write one template to generate classes which implement these interfaces(e.g: if I have 2 interfaces, the template will generate 2 classes). Generated class is put into a specific folder.
Currently, I have two issue:
1. The my template only generate one file which contain many classes.
2. The class is created below a text template.
Below is my code:
<## template language="C#" #>
<## include file="EF.Utility.CS.ttinclude"#>
<## output extension=".cs"#>
<#
Assembly assembly = Assembly.LoadFrom(#"Example.dll");
Type[] types = assembly.GetTypes();
foreach (Type type in types)
{
if (type.IsInterface)
{
string nameSpace = "Example.Client";
string className = type.Name.TrimStart('I')";
string interfaceName = type.Name;
#>
namespace <#= nameSpace #>
{
public class <#= className #> : <#= interfaceName #>
{
}
}
<#
}
}
#>
How can I generate many classes to a specific folder? Can you help me, please?
Thanks,
With your template above you would just put it in the template (.tt file) in that directory. If you want your template file to be in a higher directory look at Oleg's http://www.olegsych.com/2008/03/how-to-generate-multiple-outputs-from-single-t4-template/ Note the SaveOutput() method.
check out http://www.olegsych.com/2007/12/text-template-transformation-toolkit/
also http://www.olegsych.com/2008/03/how-to-generate-multiple-outputs-from-single-t4-template/
But why bother? If it's generated code then you shouldn't need to touch it.
Related
When I run openapi-generator-cli the Models and containing files are being output with their names containing the full namespace of the API classes. This is very awkward to read. I'd like the output to contain only the Model name, without the namespace.
E.g for class
namespace My.NameSpace.Common.V1.Models.Dto
{
public partial class PortfolioAsset
{
// properties
The output looks like
my-namespace-common-v1-models-dto-portfolio-asset.ts
With the content of the file as
export interface MyNamespaceCommonV1ModelsDtoPortfolioAsset {
// parameters
}
I just want the file to be called portfolio-asset.ts and the content to be
export interface PortfolioAsset {
// parameters
}
How can I do this?
The fix was simply to remove the CustomSchemaIds line, which specified that I should use the full name of each type being generated.
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = AppSettingsProvider.AppSettings.ApplicationName, Version = "v1" });
// c.CustomSchemaIds(x => x.FullName); <-- delete ftw
});
Currently we are using a WPF application for creation/editing of List&Label Templates, but we are considering to move to the WebDesigner. Because we use project includes we need to use the repository mode.
I've been trying to import our existing templates, but I run into some issues regarding the RepositoryItemDescriptor. To create a RepositoryItem object you have to give a Descriptor in the constructor, but I cannot find any info regarding how you get it from the generated .lst file.
The data that we have at our disposal are:
TemplateType: List or Form
TemplateData: content of the .lst file (byte[])
IsMainTemplate: bool, is a "project include" or not
File name: name of the .lst file
The RepositoryItem constructor requires: string internalID, string descriptor, string type, DateTime lastModificationUTC.
What I have now is:
public class TemplateBaseModel : RepositoryItem
{
// Properties
// we have our own Ids and modification date, override RepositoryItem properties
public new InternalID => $"repository://{{{Id}}}";
public DateTime LastModificationUTC => ModifiedOn;
public TemplateBaseModel() : base($"repository://{{{Guid.NewGuid()}}}", /* ?? */, RepositoryItemType.ProjectList.Value, DateTime.Now) { }
public TemplateBaseModel(string internalID, string descriptor, string type, DateTime lastModificationUTC) : base(internalID, descriptor, type, lastModificationUTC) { }
}
In the documentation I can only find what it is (internal metadata that is serialized into a string, and can be edited with the class RepositoryItemDescriptor), but not how it's created or how you can get it, and if I try to debug the example I get (in the CreateOrUpdate() method)#2#PgUAAENoS19QYWNrZWQAeNqd1E1PE1EYxfHfmsTvMAyJEeLY8iKCtpChU5MmvAiOC2NcjDCYmqFtZkaEqF9dXThgsTVGt/fm+Z9zz3lyv3/r2HXlQiFwKVeqDI2NdIVWPdIWCuRGTo2dGRp5ryv0Suq5yKpNoUCllhk5kymMjeS6QtdyldCuHfcs6FgUiQQSqUQgEk3dJY70pF57oS8wURo7N1TIBd64Z0GgY1HfodRA6rXAqVIgdN+SK21tbZlnt4o9J41W2OjNo9Qy72Y421OcVGzvD6R9fQcNcdb7A4WhSm3FQ4GhWu7CimUrt6T5rJvJacruHcruHEosldo38PI3ykjmQi7Qk4ilYoElJ/qOvTJwoi+Z4s33daMeeGDJiyna8szs725+zf6vmz8Tf+71U5WJzGmT/5ncucxHhdoXE6VcJVe6lFsWCGdOQzsCb+ds8I3T6R2+2/qv/ZjNvit0IjcxVhmqjZWuDZpXhHfanE2rKzSQCO0o53Ceamn5rGdTrC3Ws6YtkuiJbYts2LJlXWRbbNWayIbEE7E9sZ4Na9Y91vdVR+vWx9+9pa5NmvwKhVaTzQe5U7WWQqX+R+q+TKV20PxI54ZyZ0I7LmXK5t17PkkcOnSkdKxtT6pwLNbVnava0brt6abP1txGfwD+q8AH, which doesn't help either.
Any idea how to properly create a RepositoryItem from a .lst file? or how to create/get the descriptor?
You should try and use the class RepositoryImportUtil from the combit.ListLabel23.Repository namespace. This helper class does all the hard work for you. Given an IRepositoryinterface and the lst file in place, the required code would be something like
IRepository listLabelRepository = <yourRepository>;
using (ListLabel LL = new ListLabel())
{
LL.FileRepository = listLabelRepository;
using (RepositoryImportUtil importUtil = new RepositoryImportUtil(listLabelRepository))
{
importUtil.ImportProjectFileWithDependencies(LL,
#"<PathToRootProject>");
}
}
If this method is not what your require, the helper class has a couple of other methods as well to help you importing existing projects.
I have a simple problem and documentation is not helping me resolve it.
I have created a Grails v3.3.3 demo project - and created a simple domain class called JsonApiBook, with 'name' attribute like this
package ttrestapi
import grails.rest.*
#Resource (uri='/jsonApiBook', formats=['json','xml'])
class JsonApiBook {
static constraints = {
}
String name
}
and marked up the URI as the documentation says the JSON API rendering only works with domain classes (and not a controller class).
In my bootstrap I have saved a instance of book to the tables - and can view that generally.
In my views directory I have a created jsonApiBook folder and created two gson files.
A '_jsonApIBook' template like this
import ttrestapi.JsonApiBook
model {
JsonApiBook book
}
json jsonapi.render(book)
which invokes the jsonapi helper object to render the instance.
I have in the same directory created an index.json like this:
import ttrestapi.Book
model {
List<Book> bookList
}
// We can use template namespace
// method with a Collection.
json tmpl.book(bookList)
When I run the app and use postman or browser to render then I get a result but its Json api compliant (I think it's ignored the template).
So localhost:8080/jsonApiBook just returns (looks default layout):
[
{
"id": 1,
"name": "json api book3"
}
]
and localhost:8080/jsonApiBook/1 just returns 'null' which can't be right.
How should I be setting up the json views for rendering JSON API compliant output? As this doesn't appear to work correctly.
build.gradle
buildscript {
....
dependencies {
........
classpath "org.grails.plugins:views-gradle:1.2.7"
}
}
--
apply plugin: "org.grails.grails-web"
apply plugin: "org.grails.plugins.views-json"
dependencies {
. . .
compile "org.grails.plugins:views-json:1.2.7"
. . .
}
Domain JsonApiBook.groovy
import grails.rest.Resource
#Resource (uri='/jsonApiBook', formats=['json','xml'])
class JsonApiBook {
String name
static constraints = {
}
}
Bootstrap.groovy
class BootStrap {
def init = { servletContext ->
new JsonApiBook(name: 'first').save(flush:true)
new JsonApiBook(name: 'second').save(flush:true)
new JsonApiBook(name: 'third').save(flush:true)
new JsonApiBook(name: 'fourth').save(flush:true)
new JsonApiBook(name: 'fifth').save(flush:true)
}
def destroy = {
}
}
Created folder under view called jsonApiBook
Created template named _jsonApiBook.gson in jsonApiBook folder
model {
JsonApiBook jsonApiBook
}
json {
name jsonApiBook.name
}
created show.gson under same folder
model {
JsonApiBook jsonApiBook
}
json g.render(template:"jsonApiBook", model:[jsonApiBook:jsonApiBook])
When i run http://localhost:8080/jsonApiBook i get bellow:
When i run http://localhost:8080/jsonApiBook/1 i get bellow:
Note: I used grails 3.3.3 with h2 memory DB
Reference
Hope this helps you
ok - got to similar place today on the train. Essentially the convention over configuration is core to whats happening here.
First the #Resource annotation generates a default RestfulController for you. In this approach the default base template _resourceClassName.gson expects the model variable to have the same name as the resource type so my original example instead of 'book'
import ttrestapi.JsonApiBook
model {
JsonApiBook book
}
json jsonapi.render(book)
it should really read as (following convention)
import ttrestapi.JsonApiBook
// variable should be same name as the Class name starting with lowercase
// as default (it can be different but the caller has to change how the
// the template parameter is invoked
model {
JsonApiBook jsonApiBook
}
json jsonapi.render(jsonApiBook)
Then the index.gson should have read as modified below
import ttrestapi.JsonBookApi
//note although not obvious in the written docs which use the show command, the
// default expected model variable is <resourceClass>List
model {
List<JsonBookApi> jsonBookApiList
}
// We can use template namespace
// method with a Collection.
json tmpl.jsonBookApi (jsonBookApiList )
If you want to use another variable name then in the base template you'd have to declare that name as map when calling the base template, from the index.gson . e.g. say the variable name in the base template was
model {
JsonBookApi myBook...
then when calling this template from my index.gson you would put something like this
...
model {
List<JsonBookApi> jsonBookApiList
}
json tmpl.jsonBookApi ("myBook", jsonBookApiList )
this invokes the correct template _jsonBookApi, but takes the model variable default in the index.gson and forces it to bind the value of jsonBookApiList to the myBook variable in the base template (_jsonBookApi.gson).
With the default generation of a controller, using #Resource annotation, the model variable will always be 'resourceClassName'List
I think the only way to change that is not to use the #Resource annotation on your domain class, but to use the URL mappings configuration to map your uri to a controller, and then you have to create a controller yourself by hand and ensure you extend from RestfulController. doing this you can override the default model variable name by implementing an overidden 'index()' method and ensuring you explicitly name the model variable you want, and ensure that the index.gson model variable is exactly the same as that set in your controller.
however the key point was I was not following the core convention defaults so the code as originally built couldn't work and returned null.
when you start out the documentation isn't absolutely clear what bits are part of the convention, and in the examples (which use show.gson) don't tell you what the model variable default name will be for the index.gson (add List to end) so its quite easy to get lost
Acck I have not had to create a special stored procedure for my entity class in a while, before the current version. And then it was on smaller tables. I created my delete, great good, but I forgot that EF makes you create the insert and update as well. Anyone know of a quick way to do this? I have about 7-8 tables with about 30-50 columns in each I need to do this for, not looking forward to the typing.
CRUD stored procedure creation is awesome (and one of many features) in SSMS Tools. http://www.ssmstoolspack.com/
And then it was on smaller tables. I created my delete, great good, but I forgot that EF makes you create the insert and update as well
You tagged EntityFramework 5.0 with this question, so i dont quite understand why do you need to generate the stored procedures for delete.
All CRUD (Create, Read, Delete, Update) are done at the DbContext, with no need to write procedures for that. If you have a well organized database (with PKs, FKs and good indices) the is no need for you to have the stored procedures, because you can even compile the Views of EF so that you can have an even better result.
Se a sample code:
DbContext context = new YourContext();
public bool Delete<TEntity>(TEntity entity) where TEntity : class
{
var set = context.Set<TEntity>();
set.Attach(entity);
return true;
}
public bool Add<TEntity>(TEntity entity) where TEntity : class
{
var set = context.Set<TEntity>();
set.Add(entity);
return true;
}
You dont even need to use this approach, you can use DbContext.Set directly, like in the following example:
void Run()
{
using (DbContext context = new MyContext())
{
//Create a new person to insert
var newItem = new Person() { Name = "Mike"} ;
var set = context.Set<Person>();
set.Add(newItem);
// Returns a record from database with PK = "John"
var itemToDelete = set.Find("John");
set.Remove(itemToDelete);
// This will add the new record, and delete "John" from the Database
context.SaveChanges();
}
}
So you see, no need for stored procedures for CRUD!
Use stored procedures for other stuff, related to the database, no need for them with EF :)
Anyone know of a quick way to do this? I have about 7-8 tables with about 30-50 columns in each I need to do this for, not looking forward to the typing.
The whole point here was to show that you don't need to write stored procedures to handle CRUD operation when using EF.
You asked for a quick way of doing this (and even mentioned you dont want to type them) my answer is:
The quicker way is not do any procedure! This is fast and avoid typing! :)
The code demonstrated already handles your requirement without the need of any stored procedure. :)
Added information
If the reason you think you need stored procedures to delete is because you want to delete related entities and you think that EF does not handle this sittuation, then this is a different issue and i may tell you some possible causes:
1) Maybe you end with a error because of FK reference, in this case, please take a look here
2) Maybe the error is caused because when you are removing one entity from a relationship with other entity, EF will not understand you want do fisically delete the record, you need to excplicity alter the .ChangeState to Deleted.
Take this examle:
public static void StudentLeaves(string name)
{
using (var context = new SchoolContext())
{
context.Students.Remove(context.Students.Single(s => s.Name == name));
context.SaveChanges();
}
}
The above example will delete this student right? But noow look at the second example
public static void StudantLeaveParticularSchool(string schoolName, string name)
{
using (var context = new SchoolContext())
{
var school = context.Schools.Single(a => a.Nome == schoolName);
school .Students.Remove(context.Students.Single(a => a.Nome == name));
context.SaveChanges();
}
}
The following code will not delete the Student from the Database, it will only remove the relationship!
If you are using CodeFirst, you can explicitly say that you want to use DeleteCascade, something like this:
modelBuilder
.Entity()
.HasRequired(s => s.Schools)
.WithMany(r => r.Students)
.WillCascadeOnDelete();
UPDATED:
For those of you that are interested in havin a "magic" way of creating the stored procedures, i will tell you a way to do this, but i tell you again: If you are using EF with best practices, there is no need to do this, when i first started development using EF i also tought i needed this, so we wrote a few T4 files that generated hundreds of stored procedures in the blink of an eye. But after some time we discovered that this is not the right approach when using EF, so we dropped the procedures and took the T4 out of the project, thing got much easier.
For you to create the procedures you will need to write T4 files. If you want to know more about T4, find it here (http://msdn.microsoft.com/en-us/library/vstudio/bb126247.aspx)
I will show you the DELETE creation T4 template we wrote (you will need some other base .tt files we have to make this run, so keep in mind this is a sample):
<## template language="C#" debug="true" #>
<## output extension=".sql" #>
<## assembly name="System.Data" #>
<## assembly name="System.XML" #>
<## assembly name="Microsoft.SqlServer.ConnectionInfo" #>
<## assembly name="Microsoft.SqlServer.Smo" #>
<## assembly name="Microsoft.SqlServer.Management.Sdk.Sfc" #>
<## import namespace="System" #>
<## import namespace="System.IO" #>
<## import namespace="Microsoft.SqlServer.Management.Smo" #>
<## import namespace="Microsoft.SqlServer.Management.Common" #>
<## import namespace="System.Runtime.Remoting.Messaging" #>
-- Winsys Solutions
-- Stored Procedure de delete para a tabela <#= this.SchemaName #>.<#= this.TableName #>
CREATE PROCEDURE <#= this.SchemaName #>.<#= this.TableName.Replace("TBWS4_", "PRWS4_") #>_Delete
<# WriteParameterDeclarations(this.Table); #>
AS
BEGIN
DELETE FROM
<#= this.SchemaName #>.<#= this.TableName #>
WHERE
<# WriteWhereClause(this.Table); #>
END
GO
<## include file="CommonSqlHelper.tt" #>
<#+
/// <summary>
/// Writes stored procedure parameter declarations for all columns in the
/// primary key and all TIMESTAMP columns of the specified table.
/// </summary>
void WriteParameterDeclarations(Table table)
{
PushIndent(" ");
int parameterIndex = 0;
foreach(Column column in table.Columns)
{
if (column.InPrimaryKey || column.DataType.SqlDataType == SqlDataType.Timestamp)
{
if (parameterIndex > 0)
WriteLine(",");
Write("#{0} {1}", column.Name, GetDataTypeDeclaration(column.DataType));
parameterIndex++;
}
}
PopIndent();
}
#>
<#+
string TableName
{
get { return (string) CallContext.GetData("DeleteStoredProcedure.TableName"); }
}
string SchemaName
{
get { return (string) CallContext.GetData("DeleteStoredProcedure.SchemaName"); }
}
Table Table
{
get { return (Table) CallContext.GetData("DeleteStoredProcedure.Table"); }
}
#>
The helper methods are:
/// <summary>
/// Returns a string that contains T-SQL declaration for the specified data
/// type. For string data types this includes maximum length, for numeric
/// data types this includes scale and precision.
/// </summary>
string GetDataTypeDeclaration(DataType dataType)
{
string result = dataType.Name;
switch(dataType.SqlDataType)
{
case SqlDataType.Binary:
case SqlDataType.Char:
case SqlDataType.NChar:
case SqlDataType.NVarChar:
case SqlDataType.VarBinary:
case SqlDataType.VarChar:
result += string.Format("({0})", dataType.MaximumLength);
break;
case SqlDataType.NVarCharMax:
case SqlDataType.VarBinaryMax:
case SqlDataType.VarCharMax:
result += "(max)";
break;
case SqlDataType.Decimal:
case SqlDataType.Numeric:
result += string.Format("({0}, {1})", dataType.NumericPrecision, dataType.NumericScale);
break;
}
return result;
}
/// <summary>
/// Generates where clause for UPDATE and DELETE statements for the specified
/// table.
/// </summary>
void WriteWhereClause(Table table, bool includeAllColumns = false)
{
PushIndent(" ");
int whereIndex = 0;
foreach(Column column in table.Columns)
{
if (column.InPrimaryKey || column.DataType.SqlDataType == SqlDataType.Timestamp || includeAllColumns)
{
if (whereIndex > 0)
WriteLine(" AND");
if (includeAllColumns)
Write("({0} = #{0} OR #{0} IS NULL)", column.Name);
else
Write("{0} = #{0}", column.Name);
whereIndex++;
}
}
PopIndent();
}
In entity framework T4 templates, I can use class CodeGenerationTools.
For example:
void WriteProperty(**CodeGenerationTools** code, EdmProperty edmProperty)
{
WriteProperty(Accessibility.ForProperty(edmProperty),
code.Escape(edmProperty.TypeUsage),
code.Escape(edmProperty),
code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
}
However, I can't find where this class is defined.
Which assembly does it come from? What are its members?
Thanks
That is not class from assembly. It is included class from another template:
<## include file="EF.Utility.CS.ttinclude"#>
This files is normally stored in VS installation directory:
%VSINSTALLDIR%\Common7\IDE\Extensions\Microsoft\Entity Framework Tools\Templates\Includes