AspNetForge.WebCore
1.0.1
See the version list below for details.
dotnet add package AspNetForge.WebCore --version 1.0.1
NuGet\Install-Package AspNetForge.WebCore -Version 1.0.1
<PackageReference Include="AspNetForge.WebCore" Version="1.0.1" />
<PackageVersion Include="AspNetForge.WebCore" Version="1.0.1" />
<PackageReference Include="AspNetForge.WebCore" />
paket add AspNetForge.WebCore --version 1.0.1
#r "nuget: AspNetForge.WebCore, 1.0.1"
#:package AspNetForge.WebCore@1.0.1
#addin nuget:?package=AspNetForge.WebCore&version=1.0.1
#tool nuget:?package=AspNetForge.WebCore&version=1.0.1
AspNetForge.WebCore
Class Library для ASP.NET Core Minimal API (.NET 8).
Что делает пакет
MapCrud<TEntity, TContext>(this WebApplication app, string route)— создаёт 5 стандартных эндпоинтов: GET, GET by id, POST, PUT, DELETE.UseForgeResources(this WebApplication app, bool overwrite = false)— распаковывает встроенные JS/CSS/HTML вwwwrootприложения.- В комплект пакета входит полный пример проекта. При установке в целевой проект копируются
Models.cs,Program.csиwwwroot/.
Быстрый старт
1. Создайте проект
dotnet new web -n MyWebApp
cd MyWebApp
2. Установите пакет
dotnet add package AspNetForge.WebCore --version 1.0.1
3. Установите провайдер базы данных
Пакет не включает EF Core и PostgreSQL. Установите их самостоятельно:
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL --version 8.0.11
Если нужны JWT или Identity, установите их отдельно:
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer --version 8.0.11
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore --version 8.0.11
4. Соберите проект
В Visual Studio: Ctrl+Shift+B.
В консоли:
dotnet build
После сборки в корень проекта автоматически скопируются файлы примера:
Models.cs
Program.cs
wwwroot/
index.html
login.html
register.html
products.html
partners.html
producttypes.html
materialtypes.html
styles.css
js/crud.js
appsettings.jsonкопируется только если его ещё нет в проекте. Если файл уже был, добавьте строку подключения вручную (см. раздел 6).
5. Перезагрузите проект в Visual Studio
Visual Studio не всегда сразу показывает новые файлы. Правый клик по проекту → Перезагрузить проект (или закройте и откройте решение).
6. Настройте строку подключения
Откройте appsettings.json и добавьте:
{
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Port=5432;Database=examdb;Username=postgres;Password=postgres"
},
"Logging": { ... },
"AllowedHosts": "*"
}
Замените Username и Password на свои. База examdb должна существовать.
7. Создайте базу данных
В pgAdmin или psql:
CREATE DATABASE examdb;
Таблицы создадутся автоматически при первом обращении к API.
8. Запустите
dotnet run
Откройте http://localhost:5000 (или порт из launchSettings.json).
Если файлы не скопировались автоматически
Иногда Visual Studio не запускает build-targets пакета с первого раза. В этом случае создайте файлы вручную.
Models.cs
using Microsoft.EntityFrameworkCore;
namespace ExamProject.Models;
public class ProductType
{
public int Id { get; set; }
public required string Type { get; set; }
public required decimal Coefficient { get; set; }
}
public class MaterialType
{
public int Id { get; set; }
public required string Type { get; set; }
public required decimal PersentLoss { get; set; }
}
public class Product
{
public int Id { get; set; }
public required string Type { get; set; }
public required string Name { get; set; }
public required string Article { get; set; }
public required decimal MinCost { get; set; }
}
public class Partner
{
public int Id { get; set; }
public required string Type { get; set; }
public required string Name { get; set; }
public required string DirName { get; set; }
public required string Email { get; set; }
public required string Phone { get; set; }
public required string Address { get; set; }
public required string TIN { get; set; }
public required int Rate { get; set; }
}
public class User
{
public int Id { get; set; }
public required string Login { get; set; }
public required string Password { get; set; }
public required string FullName { get; set; }
public required string Role { get; set; }
}
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<ProductType> ProductTypes { get; set; }
public DbSet<MaterialType> MaterialTypes { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<Partner> Partners { get; set; }
public DbSet<User> Users { get; set; }
}
Program.cs
using AspNetForge.Web.Extensions;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.EntityFrameworkCore;
using ExamProject.Models;
using System.Security.Claims;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddAuthorization();
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/login.html";
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.UseForgeResources(overwrite: true);
app.UseDefaultFiles();
app.UseStaticFiles();
app.MapGet("/", () => Results.Redirect("/index.html"));
app.MapCrud<ProductType, AppDbContext>("/api/producttypes");
app.MapCrud<MaterialType, AppDbContext>("/api/materialtypes");
app.MapCrud<Product, AppDbContext>("/api/products");
app.MapCrud<Partner, AppDbContext>("/api/partners");
app.MapCrud<User, AppDbContext>("/api/users");
app.MapPost("/login", async (AppDbContext db, HttpContext context) =>
{
var form = context.Request.Form;
var login = form["login"].ToString();
var password = form["password"].ToString();
var user = await db.Users.FirstOrDefaultAsync(u =>
u.Login == login && u.Password == password);
if (user is null)
return Results.BadRequest("Логин или пароль неверный");
var claims = new ClaimsIdentity(new[]
{
new Claim(ClaimsIdentity.DefaultNameClaimType, user.Login),
new Claim(ClaimsIdentity.DefaultRoleClaimType, user.Role)
}, "Cookies");
await context.SignInAsync(new ClaimsPrincipal(claims));
return Results.Redirect("/index.html");
});
app.MapPost("/register", async (AppDbContext db, HttpContext context) =>
{
var form = context.Request.Form;
var login = form["login"].ToString();
var password = form["password"].ToString();
if (password != form["confirmPassword"].ToString())
return Results.BadRequest("Пароли не совпадают");
if (await db.Users.AnyAsync(u => u.Login == login))
return Results.BadRequest("Пользователь уже существует");
await db.Users.AddAsync(new User
{
Login = login,
Password = password,
FullName = form["fullName"].ToString(),
Role = "User"
});
await db.SaveChangesAsync();
return Results.Redirect("/login.html");
});
app.MapGet("/logout", async (HttpContext context) =>
{
await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return Results.Redirect("/login.html");
});
app.MapGet("/api/me", (HttpContext context) =>
{
if (!context.User.Identity!.IsAuthenticated)
return Results.Json(new { role = "Guest" });
return Results.Json(new
{
role = context.User.FindFirst(ClaimsIdentity.DefaultRoleClaimType)?.Value ?? "User",
name = context.User.Identity.Name
});
});
app.Run();
appsettings.json
{
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Port=5432;Database=examdb;Username=postgres;Password=postgres"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
wwwroot/styles.css
:root {
--bg: #f9fafb;
--text: #111827;
--muted: #6b7280;
--accent: #2563eb;
--danger: #dc2626;
--success: #16a34a;
--card: #ffffff;
--border: #e5e7eb;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: var(--bg);
color: var(--text);
}
header {
background: var(--card);
border-bottom: 1px solid var(--border);
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
}
header h1 { margin: 0; font-size: 1.25rem; }
nav a {
margin-left: 1rem;
color: var(--accent);
text-decoration: none;
}
main {
max-width: 1200px;
margin: 2rem auto;
padding: 0 1rem;
}
.card {
background: var(--card);
border: 1px solid var(--border);
border-radius: .75rem;
padding: 1.5rem;
margin-bottom: 1.5rem;
}
input, select, button {
padding: .5rem .75rem;
border: 1px solid var(--border);
border-radius: .5rem;
font-size: 1rem;
}
button {
background: var(--accent);
color: white;
border: none;
cursor: pointer;
}
button:hover { opacity: .9; }
button.secondary { background: var(--muted); }
button.danger { background: var(--danger); }
table {
width: 100%;
border-collapse: collapse;
margin-top: 1rem;
}
td, th {
padding: .75rem;
border-bottom: 1px solid var(--border);
text-align: left;
}
.form-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: .75rem;
margin-bottom: 1rem;
}
.auth-form {
max-width: 360px;
margin: 5rem auto;
}
.auth-form input {
width: 100%;
margin-bottom: .75rem;
}
.hidden { display: none; }
wwwroot/js/crud.js
const api = {
async get(url) {
const r = await fetch(url);
if (!r.ok) throw new Error('GET ' + url + ' failed');
return r.json();
},
async post(url, data) {
const r = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (!r.ok) throw new Error('POST failed');
return r.json().catch(() => null);
},
async put(url, data) {
const r = await fetch(url, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (!r.ok) throw new Error('PUT failed');
return r.json().catch(() => null);
},
async delete(url) {
const r = await fetch(url, { method: 'DELETE' });
if (!r.ok) throw new Error('DELETE failed');
}
};
async function renderHeader() {
try {
const me = await api.get('/api/me');
const authLink = document.getElementById('authLink');
const userInfo = document.getElementById('userInfo');
if (me.role !== 'Guest' && authLink) {
authLink.textContent = 'Выйти';
authLink.href = '/logout';
if (userInfo) userInfo.textContent = me.name + ' (' + me.role + ')';
}
} catch { }
}
function renderTable(rows, allKeys, displayKeys, onEdit, onDelete) {
const tbody = document.getElementById('tableBody');
tbody.innerHTML = '';
for (const row of rows) {
const tr = document.createElement('tr');
for (const key of displayKeys) {
const td = document.createElement('td');
td.textContent = row[key] ?? '';
tr.appendChild(td);
}
const actions = document.createElement('td');
const editBtn = document.createElement('button');
editBtn.textContent = '✎';
editBtn.onclick = () => onEdit(row);
const delBtn = document.createElement('button');
delBtn.textContent = '✕';
delBtn.className = 'danger';
delBtn.onclick = () => onDelete(row.id);
actions.appendChild(editBtn);
actions.appendChild(delBtn);
tr.appendChild(actions);
tbody.appendChild(tr);
}
}
wwwroot/index.html
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Главная</title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<header>
<h1>АспнетФордж</h1>
<nav>
<a href="/products.html">Товары</a>
<a href="/partners.html">Партнеры</a>
<a href="/producttypes.html">Типы товаров</a>
<a href="/materialtypes.html">Типы материалов</a>
<span id="userInfo"></span>
<a id="authLink" href="/login.html">Войти</a>
</nav>
</header>
<main>
<div class="card">
<h2>Добро пожаловать</h2>
<p>Это стартовый шаблон, созданный пакетом AspNetForge.WebCore.</p>
<p><strong>Важно:</strong> перед запуском создайте базу данных PostgreSQL и обновите строку подключения в <code>appsettings.json</code>.</p>
</div>
</main>
<script src="/js/crud.js"></script>
<script>renderHeader();</script>
</body>
</html>
wwwroot/login.html
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Вход</title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<main>
<div class="card auth-form">
<h2>Вход</h2>
<form action="/login" method="post">
<input name="login" placeholder="Логин" required>
<input name="password" type="password" placeholder="Пароль" required>
<button type="submit">Войти</button>
</form>
<p>Нет аккаунта? <a href="/register.html">Зарегистрироваться</a></p>
</div>
</main>
</body>
</html>
wwwroot/register.html
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Регистрация</title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<main>
<div class="card auth-form">
<h2>Регистрация</h2>
<form action="/register" method="post">
<input name="login" placeholder="Логин" required>
<input name="fullName" placeholder="ФИО" required>
<input name="password" type="password" placeholder="Пароль" required>
<input name="confirmPassword" type="password" placeholder="Повторите пароль" required>
<button type="submit">Создать аккаунт</button>
</form>
<p>Уже есть аккаунт? <a href="/login.html">Войти</a></p>
</div>
</main>
</body>
</html>
wwwroot/products.html
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Товары</title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<header>
<h1>Товары</h1>
<nav>
<a href="/index.html">Главная</a>
<span id="userInfo"></span>
<a id="authLink" href="/login.html">Войти</a>
</nav>
</header>
<main>
<div class="card">
<h3>Добавить / изменить</h3>
<form id="productForm">
<input type="hidden" id="id">
<div class="form-row">
<input id="type" name="type" placeholder="Тип" required>
<input id="name" name="name" placeholder="Название" required>
<input id="article" name="article" placeholder="Артикул" required>
<input id="minCost" name="minCost" type="number" step="0.01" placeholder="Мин. стоимость" required>
</div>
<button type="submit">Сохранить</button>
<button type="button" class="secondary" onclick="resetForm()">Отмена</button>
</form>
</div>
<div class="card">
<table>
<thead>
<tr><th>Тип</th><th>Название</th><th>Артикул</th><th>Мин. стоимость</th><th></th></tr>
</thead>
<tbody id="tableBody"></tbody>
</table>
</div>
</main>
<script src="/js/crud.js"></script>
<script>
const endpoint = '/api/products';
const fields = ['id', 'type', 'name', 'article', 'minCost'];
async function load() {
const rows = await api.get(endpoint);
renderTable(rows, fields, ['type','name','article','minCost'], editRow, deleteRow);
}
function editRow(row) {
document.getElementById('id').value = row.id;
document.getElementById('type').value = row.type;
document.getElementById('name').value = row.name;
document.getElementById('article').value = row.article;
document.getElementById('minCost').value = row.minCost;
}
function resetForm() { document.getElementById('productForm').reset(); }
document.getElementById('productForm').addEventListener('submit', async (e) => {
e.preventDefault();
const payload = {
id: Number(document.getElementById('id').value) || 0,
type: document.getElementById('type').value,
name: document.getElementById('name').value,
article: document.getElementById('article').value,
minCost: Number(document.getElementById('minCost').value)
};
if (payload.id) await api.put(`${endpoint}/${payload.id}`, payload);
else await api.post(endpoint, payload);
resetForm();
await load();
});
async function deleteRow(id) {
if (!confirm('Удалить?')) return;
await api.delete(`${endpoint}/${id}`);
await load();
}
renderHeader();
load();
</script>
</body>
</html>
wwwroot/partners.html
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Партнеры</title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<header>
<h1>Партнеры</h1>
<nav>
<a href="/index.html">Главная</a>
<span id="userInfo"></span>
<a id="authLink" href="/login.html">Войти</a>
</nav>
</header>
<main>
<div class="card">
<h3>Добавить / изменить</h3>
<form id="partnerForm">
<input type="hidden" id="id">
<div class="form-row">
<input id="type" name="type" placeholder="Тип" required>
<input id="name" name="name" placeholder="Название" required>
<input id="dirName" name="dirName" placeholder="ФИО директора" required>
<input id="email" name="email" type="email" placeholder="Email" required>
<input id="phone" name="phone" placeholder="Телефон" required>
<input id="address" name="address" placeholder="Адрес" required>
<input id="tin" name="tin" placeholder="ИНН" required>
<input id="rate" name="rate" type="number" placeholder="Рейтинг" required>
</div>
<button type="submit">Сохранить</button>
<button type="button" class="secondary" onclick="resetForm()">Отмена</button>
</form>
</div>
<div class="card">
<table>
<thead>
<tr><th>Тип</th><th>Название</th><th>Директор</th><th>Email</th><th>Телефон</th><th>Адрес</th><th>ИНН</th><th>Рейтинг</th><th></th></tr>
</thead>
<tbody id="tableBody"></tbody>
</table>
</div>
</main>
<script src="/js/crud.js"></script>
<script>
const endpoint = '/api/partners';
async function load() {
const rows = await api.get(endpoint);
renderTable(rows, ['id','type','name','dirName','email','phone','address','tin','rate'],
['type','name','dirName','email','phone','address','tin','rate'], editRow, deleteRow);
}
function editRow(row) {
['id','type','name','dirName','email','phone','address','tin','rate'].forEach(k => {
document.getElementById(k).value = row[k];
});
}
function resetForm() { document.getElementById('partnerForm').reset(); }
document.getElementById('partnerForm').addEventListener('submit', async (e) => {
e.preventDefault();
const payload = {};
['id','type','name','dirName','email','phone','address','tin','rate'].forEach(k => {
let v = document.getElementById(k).value;
if (k === 'id' || k === 'rate') v = Number(v);
payload[k] = v;
});
if (payload.id) await api.put(`${endpoint}/${payload.id}`, payload);
else await api.post(endpoint, payload);
resetForm();
await load();
});
async function deleteRow(id) {
if (!confirm('Удалить?')) return;
await api.delete(`${endpoint}/${id}`);
await load();
}
renderHeader();
load();
</script>
</body>
</html>
wwwroot/producttypes.html
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Типы товаров</title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<header>
<h1>Типы товаров</h1>
<nav>
<a href="/index.html">Главная</a>
<span id="userInfo"></span>
<a id="authLink" href="/login.html">Войти</a>
</nav>
</header>
<main>
<div class="card">
<h3>Добавить / изменить</h3>
<form id="form">
<input type="hidden" id="id">
<div class="form-row">
<input id="type" name="type" placeholder="Тип" required>
<input id="coefficient" name="coefficient" type="number" step="0.01" placeholder="Коэффициент" required>
</div>
<button type="submit">Сохранить</button>
<button type="button" class="secondary" onclick="resetForm()">Отмена</button>
</form>
</div>
<div class="card">
<table>
<thead><tr><th>Тип</th><th>Коэффициент</th><th></th></tr></thead>
<tbody id="tableBody"></tbody>
</table>
</div>
</main>
<script src="/js/crud.js"></script>
<script>
const endpoint = '/api/producttypes';
const keys = ['id','type','coefficient'];
async function load() {
const rows = await api.get(endpoint);
renderTable(rows, keys, ['type','coefficient'], editRow, deleteRow);
}
function editRow(row) {
keys.forEach(k => document.getElementById(k).value = row[k]);
}
function resetForm() { document.getElementById('form').reset(); }
document.getElementById('form').addEventListener('submit', async (e) => {
e.preventDefault();
const payload = {
id: Number(document.getElementById('id').value) || 0,
type: document.getElementById('type').value,
coefficient: Number(document.getElementById('coefficient').value)
};
if (payload.id) await api.put(`${endpoint}/${payload.id}`, payload);
else await api.post(endpoint, payload);
resetForm();
await load();
});
async function deleteRow(id) {
if (!confirm('Удалить?')) return;
await api.delete(`${endpoint}/${id}`);
await load();
}
renderHeader();
load();
</script>
</body>
</html>
wwwroot/materialtypes.html
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Типы материалов</title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<header>
<h1>Типы материалов</h1>
<nav>
<a href="/index.html">Главная</a>
<span id="userInfo"></span>
<a id="authLink" href="/login.html">Войти</a>
</nav>
</header>
<main>
<div class="card">
<h3>Добавить / изменить</h3>
<form id="form">
<input type="hidden" id="id">
<div class="form-row">
<input id="type" name="type" placeholder="Тип" required>
<input id="persentLoss" name="persentLoss" type="number" step="0.01" placeholder="Процент потерь" required>
</div>
<button type="submit">Сохранить</button>
<button type="button" class="secondary" onclick="resetForm()">Отмена</button>
</form>
</div>
<div class="card">
<table>
<thead><tr><th>Тип</th><th>% потерь</th><th></th></tr></thead>
<tbody id="tableBody"></tbody>
</table>
</div>
</main>
<script src="/js/crud.js"></script>
<script>
const endpoint = '/api/materialtypes';
const keys = ['id','type','persentLoss'];
async function load() {
const rows = await api.get(endpoint);
renderTable(rows, keys, ['type','persentLoss'], editRow, deleteRow);
}
function editRow(row) {
keys.forEach(k => document.getElementById(k).value = row[k]);
}
function resetForm() { document.getElementById('form').reset(); }
document.getElementById('form').addEventListener('submit', async (e) => {
e.preventDefault();
const payload = {
id: Number(document.getElementById('id').value) || 0,
type: document.getElementById('type').value,
persentLoss: Number(document.getElementById('persentLoss').value)
};
if (payload.id) await api.put(`${endpoint}/${payload.id}`, payload);
else await api.post(endpoint, payload);
resetForm();
await load();
});
async function deleteRow(id) {
if (!confirm('Удалить?')) return;
await api.delete(`${endpoint}/${id}`);
await load();
}
renderHeader();
load();
</script>
</body>
</html>
Возможные ошибки и решения
Visual Studio предлагает запустить прошлый вариант
Это значит, что проект ещё не пересобран с новыми файлами.
- Нажмите Ctrl+Shift+B (собрать проект).
- Правый клик по проекту → Перезагрузить проект.
- Запустите снова.
CS0246: Не удалось найти тип или имя пространства имен "ExamProject"
Файл Models.cs не попал в компиляцию. Убедитесь, что он лежит в корне проекта. Если файла нет — создайте его вручную из раздела выше.
CS0234: Тип или имя пространства имен "EntityFrameworkCore" не существует
Не установлен пакет PostgreSQL:
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL --version 8.0.11
Npgsql.PostgresException: 28P01: password authentication failed
Неверный логин или пароль в appsettings.json. Проверьте значения Username и Password.
Npgsql.PostgresException: 3D000: database "examdb" does not exist
База данных не создана. Создайте в pgAdmin/psql:
CREATE DATABASE examdb;
404 Not Found при открытии страницы
Убедитесь, что в Program.cs есть:
app.UseDefaultFiles();
app.UseStaticFiles();
app.MapGet("/", () => Results.Redirect("/index.html"));
А также что папка wwwroot существует и содержит index.html.
wwwroot пустой
При старте приложения UseForgeResources(overwrite: true) скопирует встроенные файлы в wwwroot. Если этого не произошло, проверьте, что вы вызвали метод до UseStaticFiles().
Сборка пакета из исходников
cd tmp/AspNetForge.Web
dotnet build -c Release
.nupkg появится в bin/Release/.
Структура пакета
lib/net8.0/AspNetForge.Web.dll
build/AspNetForge.WebCore.targets
contentFiles/any/any/Sample/Models.cs
contentFiles/any/any/Sample/Program.cs
contentFiles/any/any/Sample/appsettings.json
contentFiles/any/any/Sample/wwwroot/...
build/AspNetForge.WebCore.targets копирует файлы примера из пакета в целевой проект при сборке.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.