eQuantic.UIeQuantic.UI
Docs
Playground
GitHub
Docspt-BR
Resolução de tipos externos no compilador do eQuantic.UI
Edit this page
2 min read
🌐 Esta página em: English · Português
Visão geral
O compilador do eQuantic.UI resolve tipos definidos em arquivos externos dentro do mesmo projeto. Os componentes referenciam modelos, DTOs e outras classes sem exigir que elas estejam no mesmo arquivo: o compilador trabalha contra a Compilation Roslyn completa do projeto, que inclui todos os arquivos de código e as referências.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Models/User.cs
public class User
{
public string Name { get; set; }
public string Email { get; set; }
}
// Pages/UserProfile.cs
[Page("/profile")]
public class UserProfile : StatefulComponent
{
private User _currentUser; // ✅ Tipo 'User' totalmente resolvido
protected override HtmlNode Render()
{
return Text(_currentUser.Name); // ✅ Converte para: this._currentUser.name
}
}
Um semantic model completo importa porque a conversão de acesso a membro depende de saber o tipo do receptor: com ele, _currentUser.Name rebaixa para this._currentUser.name; sem ele, o compilador não consegue distinguir uma propriedade de uma variável local nem escolher o mapeamento de método certo.
Uso da API
Opção 1: usando o MSBuildWorkspace (recomendado)
1
2
3
4
5
6
7
8
9
10
11
12
13
using eQuantic.UI.Compiler;
using eQuantic.UI.Compiler.Services;
// Obtém a compilação completa do projeto
var compilation = await ProjectCompilationHelper
.GetProjectCompilationAsync("path/to/MyApp.csproj");
// Cria o compilador e define a compilação do projeto
var compiler = new ComponentCompiler();
compiler.SetProjectCompilation(compilation);
// Agora compile os componentes: os tipos externos serão resolvidos
var results = compiler.CompileFile("Pages/UserProfile.cs");
Opção 2: compilação manual a partir das fontes
Útil em tasks MSBuild onde o MSBuildWorkspace pode não estar disponível:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using eQuantic.UI.Compiler.Services;
// Obtém todos os arquivos .cs do projeto
var sourceFiles = ProjectCompilationHelper
.GetProjectSourceFiles("path/to/MyApp");
// Obtém as referências de assembly
var assemblyPaths = new[]
{
"path/to/eQuantic.UI.Core.dll",
"path/to/other-dependencies.dll"
};
// Cria a compilação a partir das fontes
var compilation = ProjectCompilationHelper.CreateCompilationFromSources(
sourceFiles,
assemblyPaths,
"MyApp");
var compiler = new ComponentCompiler();
compiler.SetProjectCompilation(compilation);
Opção 3: limpando a compilação do projeto
Volta ao modo de compilação mínima (arquivos isolados):
1
compiler.ClearProjectCompilation();
Como funciona
Arquitetura
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌─────────────────────────────────────────┐
│ Compilação do projeto no MSBuild │
│ - Todos os .cs do projeto │
│ - Todos os assemblies referenciados │
│ - Informação de tipo completa │
└────────────┬────────────────────────────┘
↓ SetProjectCompilation()
┌────────────────────────────────────────┐
│ SemanticModelProvider │
│ - Guarda a compilação do projeto │
│ - Devolve um SemanticModel por arquivo│
└────────────┬───────────────────────────┘
↓ GetSemanticModel(tree)
┌────────────────────────────────────────┐
│ CSharpToJsConverter │
│ - Usa o SemanticModel para os tipos │
│ - Converte _currentUser.Name → name │
└────────────────────────────────────────┘
Classes-chave
1.
SemanticModelProvider (SemanticModelProvider.cs)
SetProjectCompilation(Compilation) - define a compilação completa do projeto
GetSemanticModel(SyntaxTree) - devolve o semantic model com informação de tipo completa
2.
ProjectCompilationHelper (ProjectCompilationHelper.cs)
GetProjectCompilationAsync(string) - carrega a partir do .csproj
CreateCompilationFromSources(...) - constrói a partir dos arquivos de código
GetProjectSourceFiles(string) - encontra todos os arquivos .cs
3.
ComponentCompiler (ComponentCompiler.cs)
SetProjectCompilation(Compilation) - liga a resolução de tipos externos
ClearProjectCompilation() - volta ao modo isolado
Exemplos
Exemplo 1: componente com modelo externo
Models/Product.cs:
1
2
3
4
5
6
7
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public bool InStock { get; set; }
}
Pages/ProductCard.cs:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Component]
public class ProductCard : StatelessComponent
{
public Product Item { get; set; }
protected override HtmlNode Build()
{
return Container(
Heading(Item.Name),
Text($"${Item.Price:F2}"),
Text(Item.InStock ? "In Stock" : "Out of Stock")
);
}
}
JavaScript gerado (com a compilação do projeto):
1
2
3
4
5
6
7
8
9
class ProductCard extends StatelessComponent {
build() {
return Container([
Heading(this.item.name),
Text(`$${this.item.price.toFixed(2)}`),
Text(this.item.inStock ? 'In Stock' : 'Out of Stock')
]);
}
}
Exemplo 2: vários tipos externos
Models/Address.cs:
1
2
3
4
5
6
public class Address
{
public string Street { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
}
Models/Customer.cs:
1
2
3
4
5
6
public class Customer
{
public string Name { get; set; }
public string Email { get; set; }
public Address ShippingAddress { get; set; }
}
Pages/CheckoutPage.cs:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Page("/checkout")]
public class CheckoutPage : StatefulComponent
{
private Customer _customer;
protected override HtmlNode Render()
{
return Container(
Text($"Customer: {_customer.Name}"),
Text($"Email: {_customer.Email}"),
Text($"Shipping: {_customer.ShippingAddress.City}, {_customer.ShippingAddress.ZipCode}")
);
}
}
Todos os acessos a propriedade são resolvidos corretamente e convertidos para JavaScript.
Testes
Os testes verificam que a resolução de tipos externos funciona corretamente:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[Fact]
public void SemanticModel_WithProjectCompilation_CanResolveExternalTypes()
{
// Cria a compilação com User e o componente
var userTree = CSharpSyntaxTree.ParseText("public class User { ... }");
var componentTree = CSharpSyntaxTree.ParseText("public class UserProfile { ... }");
var compilation = CSharpCompilation.Create("Test", new[] { userTree, componentTree }, ...);
var provider = new SemanticModelProvider();
provider.SetProjectCompilation(compilation);
var semanticModel = provider.GetSemanticModel(componentTree);
// Afirma: o tipo User está resolvido
var userType = semanticModel.Compilation.GetTypeByMetadataName("User");
userType.Should().NotBeNull();
}
Veja o ExternalTypeResolutionTests.cs para a suíte de testes completa.
Modos
Sem SetProjectCompilation(): compilação mínima, então cada arquivo é resolvido isoladamente
Com SetProjectCompilation(): resolução completa de tipos do projeto
Performance
Custo mínimo:
A compilação do projeto é criada uma vez, em tempo de build
Compartilhada entre todos os arquivos de componente
Sem penalidade de compilação por arquivo
Eficiente em memória:
Uma única instância de compilação
Reaproveita a infraestrutura existente do Roslyn
Limitações
1.
Exige compilação antes da compilação dos componentes: o projeto tem que estar compilado (ou pelo menos parseado) antes de rodar o compilador de componentes.
2.
Disponibilidade do MSBuildWorkspace: o GetProjectCompilationAsync precisa das APIs do MSBuild, que podem não estar disponíveis em todos os contextos. Use o CreateCompilationFromSources como alternativa.
3.
Código gerado: arquivos autogerados em obj/ são excluídos para evitar conflitos.
Resumo
Componentes referenciam classes de outros arquivos; a informação de tipo completa está disponível durante a compilação, então a geração de JavaScript para membros de tipos externos sai correta.
O build do SDK entrega ao compilador as referências de assembly reais do projeto (--refs), então os tipos de receptor resolvem mesmo atravessando fronteiras de pacote.
1
2
var compilation = await ProjectCompilationHelper.GetProjectCompilationAsync(projectPath);
compiler.SetProjectCompilation(compilation);