386 lines
15 KiB
C#
386 lines
15 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Data;
|
|||
|
|
using System.IO;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Threading.Tasks;
|
|||
|
|
using Dapper;
|
|||
|
|
using Microsoft.Data.SqlClient;
|
|||
|
|
using MotoresArgentinosV2.MigrationTool.Models;
|
|||
|
|
|
|||
|
|
namespace MotoresArgentinosV2.MigrationTool;
|
|||
|
|
|
|||
|
|
public class AdMigrator
|
|||
|
|
{
|
|||
|
|
private readonly string _connStringAutos;
|
|||
|
|
private readonly string _connStringV2;
|
|||
|
|
|
|||
|
|
public AdMigrator(string connAutos, string connV2)
|
|||
|
|
{
|
|||
|
|
_connStringAutos = connAutos;
|
|||
|
|
_connStringV2 = connV2;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public async Task ExecuteAsync()
|
|||
|
|
{
|
|||
|
|
Console.WriteLine("🚗 INICIANDO MIGRACIÓN DE AUTOS...");
|
|||
|
|
|
|||
|
|
using var dbAutos = new SqlConnection(_connStringAutos);
|
|||
|
|
using var dbV2 = new SqlConnection(_connStringV2);
|
|||
|
|
|
|||
|
|
// 1. CARGAR CACHÉS DE V2
|
|||
|
|
Console.WriteLine(" 📥 Cargando diccionarios de Marcas, Modelos y Usuarios...");
|
|||
|
|
|
|||
|
|
var brandsV2 = (await dbV2.QueryAsync("SELECT BrandID, LegacyID, Name FROM Brands WHERE VehicleTypeID = 1")).ToList();
|
|||
|
|
var usersV2 = (await dbV2.QueryAsync("SELECT UserID, Email, PhoneNumber FROM Users")).ToDictionary(u => (string)u.Email, u => u);
|
|||
|
|
|
|||
|
|
// 2. OBTENER AVISOS LEGACY (QUERY MEJORADA PARA ADMINS)
|
|||
|
|
Console.WriteLine(" 📥 Leyendo avisos activos del Legacy...");
|
|||
|
|
|
|||
|
|
// Esta query intenta obtener el email de dos fuentes:
|
|||
|
|
// 1. De la tabla Particulares (prioridad)
|
|||
|
|
// 2. De la tabla de autenticación (aspnet_Membership) si no está en Particulares
|
|||
|
|
var queryAds = @"
|
|||
|
|
SELECT
|
|||
|
|
A.Auto_Id,
|
|||
|
|
A.Auto_Fecha_Publicacion,
|
|||
|
|
A.Auto_Cant_Visitas,
|
|||
|
|
COALESCE(P.Part_Mail, Mem.Email) as EmailVendedor,
|
|||
|
|
M.Marca_Detalle,
|
|||
|
|
Mo.Modelo_Detalle,
|
|||
|
|
A.Auto_Version,
|
|||
|
|
An.Año_Detalle,
|
|||
|
|
A.Auto_precio,
|
|||
|
|
Mon.Mone_Detalle,
|
|||
|
|
A.Auto_Kilometros,
|
|||
|
|
A.Auto_Detalle as Descripcion,
|
|||
|
|
C.Comb_Detalle,
|
|||
|
|
Col.Color_detalle,
|
|||
|
|
Car.Carroceria_Nombre,
|
|||
|
|
CASE WHEN D.DetAut_PapelesALDia = 1 THEN 1 ELSE 0 END as PapelesAlDia,
|
|||
|
|
COALESCE(P.Part_Telefono, '') as Part_Telefono,
|
|||
|
|
COALESCE(P.Part_Celular, '') as Part_Celular
|
|||
|
|
FROM Autos A
|
|||
|
|
-- Join original con particulares
|
|||
|
|
LEFT JOIN Particulares P ON A.Auto_Usuario_Id = P.Part_Usu_Nombre
|
|||
|
|
-- Join adicional para rescatar usuarios admin/internos sin perfil particular
|
|||
|
|
LEFT JOIN aspnet_Users U ON A.Auto_Usuario_Id = U.UserName
|
|||
|
|
LEFT JOIN aspnet_Membership Mem ON U.UserId = Mem.UserId
|
|||
|
|
|
|||
|
|
-- Joins de datos técnicos
|
|||
|
|
LEFT JOIN Marca M ON A.Auto_Marca_Id = M.Marca_Id
|
|||
|
|
LEFT JOIN Modelo Mo ON A.Auto_Modelo_Id = Mo.Modelo_Id
|
|||
|
|
LEFT JOIN Año An ON A.Auto_Año = An.Año_Id
|
|||
|
|
LEFT JOIN Combustible C ON A.Auto_Comb_Id = C.Comb_Id
|
|||
|
|
LEFT JOIN Color Col ON A.Auto_Color = Col.Color_Id
|
|||
|
|
LEFT JOIN Carroceria Car ON A.Auto_Carroceria_Id = Car.Carroceria_Id
|
|||
|
|
LEFT JOIN Moneda Mon ON A.Auto_Moneda_Id = Mon.Mone_id
|
|||
|
|
LEFT JOIN Detalle_Auto D ON A.Auto_Id = D.DetAut_Auto_Id
|
|||
|
|
|
|||
|
|
WHERE A.Auto_Estado = 0
|
|||
|
|
AND A.Auto_Tipo_Id = 1
|
|||
|
|
AND A.Auto_Fecha_Finalizacion >= GETDATE()";
|
|||
|
|
|
|||
|
|
var adsLegacy = (await dbAutos.QueryAsync<LegacyAdData>(queryAds)).ToList();
|
|||
|
|
Console.WriteLine($" ✅ Encontrados {adsLegacy.Count} avisos candidatos.");
|
|||
|
|
|
|||
|
|
// 3. OBTENER FOTOS
|
|||
|
|
var idsString = string.Join(",", adsLegacy.Select(a => a.Auto_Id));
|
|||
|
|
var photosLegacy = new List<LegacyAdPhoto>();
|
|||
|
|
if (adsLegacy.Any())
|
|||
|
|
{
|
|||
|
|
photosLegacy = (await dbAutos.QueryAsync<LegacyAdPhoto>(
|
|||
|
|
$"SELECT Foto_Id, Foto_Auto_Id, Foto_Ruta FROM Fotos_Autos WHERE Foto_Auto_Id IN ({idsString})")).ToList();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int migrados = 0;
|
|||
|
|
int omitidosUsuario = 0;
|
|||
|
|
|
|||
|
|
foreach (var ad in adsLegacy)
|
|||
|
|
{
|
|||
|
|
// A. VALIDAR USUARIO
|
|||
|
|
string emailKey = ad.EmailVendedor?.ToLower().Trim() ?? "";
|
|||
|
|
|
|||
|
|
if (!usersV2.TryGetValue(emailKey, out var userV2))
|
|||
|
|
{
|
|||
|
|
omitidosUsuario++;
|
|||
|
|
// Console.WriteLine($" ⚠️ Aviso {ad.Auto_Id} omitido: Usuario {emailKey} no existe en V2.");
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// B. RESOLVER MARCA Y MODELO
|
|||
|
|
var brandV2 = brandsV2.FirstOrDefault(b => b.Name.ToLower() == (ad.Marca_Detalle ?? "").ToLower());
|
|||
|
|
if (brandV2 == null)
|
|||
|
|
{
|
|||
|
|
// Si la marca no existe, saltamos por seguridad o podríamos asignar "Otros"
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int modelId = await GetOrCreateModelAsync(dbV2, (int)brandV2.BrandID, ad.Modelo_Detalle);
|
|||
|
|
|
|||
|
|
// C. CONSTRUIR DATOS
|
|||
|
|
int.TryParse(ad.Año_Detalle, out int year);
|
|||
|
|
|
|||
|
|
string versionName = $"{ad.Modelo_Detalle} {ad.Auto_Version}".Trim();
|
|||
|
|
if (versionName.Length > 100) versionName = versionName.Substring(0, 100);
|
|||
|
|
|
|||
|
|
string desc = ad.Descripcion ?? "";
|
|||
|
|
if (ad.PapelesAlDia == 1) desc += "\n\n✔️ Papeles al día.";
|
|||
|
|
|
|||
|
|
// Contacto: Si venía de Particulares, usamos eso. Si no (admins), usamos el del usuario V2
|
|||
|
|
string contactPhone = !string.IsNullOrWhiteSpace(ad.Part_Celular) ? ad.Part_Celular : ad.Part_Telefono;
|
|||
|
|
if (string.IsNullOrWhiteSpace(contactPhone)) contactPhone = userV2.PhoneNumber;
|
|||
|
|
|
|||
|
|
string currency = (ad.Mone_Detalle != null && ad.Mone_Detalle.Contains("US")) ? "USD" : "ARS";
|
|||
|
|
|
|||
|
|
// D. INSERTAR AVISO
|
|||
|
|
var insertAdSql = @"
|
|||
|
|
INSERT INTO Ads (
|
|||
|
|
UserID, VehicleTypeID, BrandID, ModelID, VersionName, Year, KM,
|
|||
|
|
Price, Currency, Description, IsFeatured, StatusID, CreatedAt, PublishedAt, ExpiresAt,
|
|||
|
|
FuelType, Color, Segment, Location, Condition, Transmission, Steering,
|
|||
|
|
ContactPhone, ContactEmail, DisplayContactInfo, LegacyAdID,
|
|||
|
|
ViewsCounter
|
|||
|
|
) VALUES (
|
|||
|
|
@UserID, @VehicleTypeID, @BrandID, @ModelID, @VersionName, @Year, @KM,
|
|||
|
|
@Price, @Currency, @Description, 0, 4, @CreatedAt, @PublishedAt, DATEADD(day, 30, @PublishedAt),
|
|||
|
|
@FuelType, @Color, @Segment, 'La Plata', 'No Especificado', 'No Especificado', 'No Especificado',
|
|||
|
|
@ContactPhone, @ContactEmail, 1, @LegacyAdID,
|
|||
|
|
@ViewsCounter
|
|||
|
|
);
|
|||
|
|
SELECT CAST(SCOPE_IDENTITY() as int);";
|
|||
|
|
|
|||
|
|
int newAdId = await dbV2.ExecuteScalarAsync<int>(insertAdSql, new
|
|||
|
|
{
|
|||
|
|
UserID = userV2.UserID,
|
|||
|
|
VehicleTypeID = 1,
|
|||
|
|
BrandID = brandV2.BrandID,
|
|||
|
|
ModelID = modelId,
|
|||
|
|
VersionName = versionName,
|
|||
|
|
Year = year,
|
|||
|
|
KM = ad.Auto_Kilometros,
|
|||
|
|
Price = ad.Auto_precio,
|
|||
|
|
Currency = currency,
|
|||
|
|
Description = desc,
|
|||
|
|
FuelType = ad.Comb_Detalle,
|
|||
|
|
Color = ad.Color_detalle,
|
|||
|
|
Segment = ad.Carroceria_Nombre,
|
|||
|
|
ContactPhone = contactPhone,
|
|||
|
|
ContactEmail = userV2.Email,
|
|||
|
|
LegacyAdID = ad.Auto_Id,
|
|||
|
|
ViewsCounter = ad.Auto_Cant_Visitas,
|
|||
|
|
CreatedAt = ad.Auto_Fecha_Publicacion,
|
|||
|
|
PublishedAt = ad.Auto_Fecha_Publicacion
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// E. INSERTAR FOTOS
|
|||
|
|
var misFotos = photosLegacy
|
|||
|
|
.Where(p => p.Foto_Auto_Id == ad.Auto_Id && !p.Foto_Ruta.Contains("nofoto"))
|
|||
|
|
.ToList();
|
|||
|
|
|
|||
|
|
int order = 0;
|
|||
|
|
foreach (var foto in misFotos)
|
|||
|
|
{
|
|||
|
|
string fileName = Path.GetFileName(foto.Foto_Ruta);
|
|||
|
|
// Aquí podrías agregar la lógica para copiar el archivo físico si tienes acceso
|
|||
|
|
// Por ahora solo guardamos la referencia en DB
|
|||
|
|
string v2Path = $"/uploads/legacy/{fileName}";
|
|||
|
|
|
|||
|
|
await dbV2.ExecuteAsync(@"
|
|||
|
|
INSERT INTO AdPhotos (AdID, FilePath, IsCover, SortOrder)
|
|||
|
|
VALUES (@AdID, @FilePath, @IsCover, @SortOrder)",
|
|||
|
|
new { AdID = newAdId, FilePath = v2Path, IsCover = (order == 0), SortOrder = order });
|
|||
|
|
|
|||
|
|
order++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
migrados++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Console.WriteLine("==================================================");
|
|||
|
|
Console.WriteLine($"🏁 MIGRACIÓN DE AUTOS FINALIZADA");
|
|||
|
|
Console.WriteLine($"✅ Insertados: {migrados}");
|
|||
|
|
Console.WriteLine($"⏩ Omitidos (Sin usuario/marca): {omitidosUsuario}");
|
|||
|
|
Console.WriteLine("==================================================");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public async Task MigrateMotosAsync()
|
|||
|
|
{
|
|||
|
|
Console.WriteLine("\n🏍️ INICIANDO MIGRACIÓN DE MOTOS...");
|
|||
|
|
|
|||
|
|
using var dbAutos = new SqlConnection(_connStringAutos);
|
|||
|
|
using var dbV2 = new SqlConnection(_connStringV2);
|
|||
|
|
|
|||
|
|
// 1. CARGAR CACHÉS (Solo Marcas de Motos)
|
|||
|
|
Console.WriteLine(" 📥 Cargando diccionarios...");
|
|||
|
|
var brandsV2 = (await dbV2.QueryAsync("SELECT BrandID, LegacyID, Name FROM Brands WHERE VehicleTypeID = 2")).ToList(); // 2 = Motos
|
|||
|
|
var usersV2 = (await dbV2.QueryAsync("SELECT UserID, Email, PhoneNumber FROM Users")).ToDictionary(u => (string)u.Email, u => u);
|
|||
|
|
|
|||
|
|
// 2. QUERY MOTOS
|
|||
|
|
var query = @"
|
|||
|
|
SELECT
|
|||
|
|
M.Moto_Id,
|
|||
|
|
M.Moto_Fecha_Publicacion,
|
|||
|
|
M.Moto_Cant_Visitas,
|
|||
|
|
COALESCE(P.Part_Mail, Mem.Email) as EmailVendedor,
|
|||
|
|
|
|||
|
|
Ma.Marca_Moto_Detalle as Marca_Detalle,
|
|||
|
|
Mo.Modelo_Moto_Detalle as Modelo_Detalle,
|
|||
|
|
|
|||
|
|
An.Año_Detalle,
|
|||
|
|
M.Moto_Precio,
|
|||
|
|
Mon.Mone_Detalle,
|
|||
|
|
M.Moto_Kilometraje,
|
|||
|
|
M.Moto_Detalle as Descripcion,
|
|||
|
|
M.Moto_Cilindrada,
|
|||
|
|
M.Moto_Tipo_Cuatri_Id,
|
|||
|
|
|
|||
|
|
COALESCE(P.Part_Telefono, '') as Part_Telefono,
|
|||
|
|
COALESCE(P.Part_Celular, '') as Part_Celular
|
|||
|
|
FROM Motos M
|
|||
|
|
LEFT JOIN Particulares P ON M.Moto_Usuario_Id = P.Part_Usu_Nombre
|
|||
|
|
LEFT JOIN aspnet_Users U ON M.Moto_Usuario_Id = U.UserName
|
|||
|
|
LEFT JOIN aspnet_Membership Mem ON U.UserId = Mem.UserId
|
|||
|
|
|
|||
|
|
-- JOINS TÉCNICOS
|
|||
|
|
LEFT JOIN Marca_Moto Ma ON M.Moto_Marca_Id = Ma.Marca_Moto_Id
|
|||
|
|
LEFT JOIN Modelo_Moto Mo ON M.Moto_Modelo_Id = Mo.Modelo_Moto_Id
|
|||
|
|
LEFT JOIN Año An ON M.Moto_Año = An.Año_Id
|
|||
|
|
LEFT JOIN Moneda Mon ON M.Moto_Moneda_Id = Mon.Mone_id
|
|||
|
|
|
|||
|
|
WHERE M.Moto_Estado = 0
|
|||
|
|
AND M.Moto_Fecha_Finalizacion >= GETDATE()";
|
|||
|
|
|
|||
|
|
var adsLegacy = (await dbAutos.QueryAsync<LegacyMotoData>(query)).ToList();
|
|||
|
|
Console.WriteLine($" ✅ Encontradas {adsLegacy.Count} motos candidatas.");
|
|||
|
|
|
|||
|
|
// 3. FOTOS
|
|||
|
|
var idsString = string.Join(",", adsLegacy.Select(a => a.Moto_Id));
|
|||
|
|
var photosLegacy = new List<LegacyMotoPhoto>();
|
|||
|
|
if (adsLegacy.Any())
|
|||
|
|
{
|
|||
|
|
photosLegacy = (await dbAutos.QueryAsync<LegacyMotoPhoto>(
|
|||
|
|
$"SELECT Foto_Id, Foto_Moto_Id, Foto_Ruta FROM Fotos_Motos WHERE Foto_Moto_Id IN ({idsString})")).ToList();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int migrados = 0;
|
|||
|
|
int omitidos = 0;
|
|||
|
|
|
|||
|
|
foreach (var ad in adsLegacy)
|
|||
|
|
{
|
|||
|
|
// A. Validar Usuario
|
|||
|
|
string emailKey = ad.EmailVendedor?.ToLower().Trim() ?? "";
|
|||
|
|
if (!usersV2.TryGetValue(emailKey, out var userV2))
|
|||
|
|
{
|
|||
|
|
omitidos++;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// B. Resolver Marca y Modelo
|
|||
|
|
var brandV2 = brandsV2.FirstOrDefault(b => b.Name.ToLower() == (ad.Marca_Detalle ?? "").ToLower());
|
|||
|
|
if (brandV2 == null) continue; // Marca no mapeada, saltar
|
|||
|
|
|
|||
|
|
int modelId = await GetOrCreateModelAsync(dbV2, (int)brandV2.BrandID, ad.Modelo_Detalle);
|
|||
|
|
|
|||
|
|
// C. Datos
|
|||
|
|
int.TryParse(ad.Año_Detalle, out int year);
|
|||
|
|
string currency = (ad.Mone_Detalle != null && ad.Mone_Detalle.Contains("US")) ? "USD" : "ARS";
|
|||
|
|
|
|||
|
|
// Nombre Versión: Concatenamos Modelo + Cilindrada si existe
|
|||
|
|
string versionName = ad.Modelo_Detalle;
|
|||
|
|
// La cilindrada la ponemos en la descripción como pediste, pero a veces queda bien en el titulo.
|
|||
|
|
// Lo dejamos en el título solo si es muy corto, si no, description.
|
|||
|
|
|
|||
|
|
// Descripción: Concatenar Cilindrada
|
|||
|
|
string desc = ad.Descripcion ?? "";
|
|||
|
|
if (!string.IsNullOrEmpty(ad.Moto_Cilindrada) && ad.Moto_Cilindrada != "0")
|
|||
|
|
{
|
|||
|
|
desc += $"\n\nCilindrada: {ad.Moto_Cilindrada}cc";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Segmento: Lógica de Cuatriciclo
|
|||
|
|
string segment = ad.Moto_Tipo_Cuatri_Id > 0 ? "Cuatriciclo" : "No Especificado";
|
|||
|
|
|
|||
|
|
// Contacto
|
|||
|
|
string contactPhone = !string.IsNullOrWhiteSpace(ad.Part_Celular) ? ad.Part_Celular : ad.Part_Telefono;
|
|||
|
|
if (string.IsNullOrWhiteSpace(contactPhone)) contactPhone = userV2.PhoneNumber;
|
|||
|
|
|
|||
|
|
// D. Insertar
|
|||
|
|
var insertSql = @"
|
|||
|
|
INSERT INTO Ads (
|
|||
|
|
UserID, VehicleTypeID, BrandID, ModelID, VersionName, Year, KM,
|
|||
|
|
Price, Currency, Description, IsFeatured, StatusID, CreatedAt, PublishedAt, ExpiresAt,
|
|||
|
|
FuelType, Color, Segment, Location, Condition, Transmission, Steering,
|
|||
|
|
ContactPhone, ContactEmail, DisplayContactInfo, LegacyAdID, ViewsCounter
|
|||
|
|
) VALUES (
|
|||
|
|
@UserID, 2, @BrandID, @ModelID, @VersionName, @Year, @KM,
|
|||
|
|
@Price, @Currency, @Description, 0, 4, @CreatedAt, @PublishedAt, DATEADD(day, 30, @PublishedAt),
|
|||
|
|
'Nafta', 'No Especificado', @Segment, 'La Plata', 'No Especificado', 'Manual', 'No Especificado',
|
|||
|
|
@ContactPhone, @ContactEmail, 1, @LegacyAdID, @ViewsCounter
|
|||
|
|
);
|
|||
|
|
SELECT CAST(SCOPE_IDENTITY() as int);";
|
|||
|
|
|
|||
|
|
int newAdId = await dbV2.ExecuteScalarAsync<int>(insertSql, new
|
|||
|
|
{
|
|||
|
|
UserID = userV2.UserID,
|
|||
|
|
BrandID = brandV2.BrandID,
|
|||
|
|
ModelID = modelId,
|
|||
|
|
VersionName = versionName,
|
|||
|
|
Year = year,
|
|||
|
|
KM = ad.Moto_Kilometraje,
|
|||
|
|
Price = ad.Moto_Precio,
|
|||
|
|
Currency = currency,
|
|||
|
|
Description = desc,
|
|||
|
|
Segment = segment,
|
|||
|
|
ContactPhone = contactPhone,
|
|||
|
|
ContactEmail = userV2.Email,
|
|||
|
|
LegacyAdID = ad.Moto_Id,
|
|||
|
|
ViewsCounter = ad.Moto_Cant_Visitas,
|
|||
|
|
CreatedAt = ad.Moto_Fecha_Publicacion,
|
|||
|
|
PublishedAt = ad.Moto_Fecha_Publicacion
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// E. Fotos
|
|||
|
|
var misFotos = photosLegacy
|
|||
|
|
.Where(p => p.Foto_Moto_Id == ad.Moto_Id && !p.Foto_Ruta.Contains("nofoto"))
|
|||
|
|
.ToList();
|
|||
|
|
|
|||
|
|
int order = 0;
|
|||
|
|
foreach (var foto in misFotos)
|
|||
|
|
{
|
|||
|
|
string fileName = Path.GetFileName(foto.Foto_Ruta);
|
|||
|
|
string v2Path = $"/uploads/legacy/{fileName}";
|
|||
|
|
|
|||
|
|
await dbV2.ExecuteAsync(@"
|
|||
|
|
INSERT INTO AdPhotos (AdID, FilePath, IsCover, SortOrder)
|
|||
|
|
VALUES (@AdID, @FilePath, @IsCover, @SortOrder)",
|
|||
|
|
new { AdID = newAdId, FilePath = v2Path, IsCover = (order == 0), SortOrder = order });
|
|||
|
|
|
|||
|
|
order++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
migrados++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Console.WriteLine("==================================================");
|
|||
|
|
Console.WriteLine($"🏁 MIGRACIÓN MOTOS FINALIZADA: {migrados} insertados.");
|
|||
|
|
Console.WriteLine("==================================================");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async Task<int> GetOrCreateModelAsync(SqlConnection db, int brandId, string modelName)
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrWhiteSpace(modelName)) modelName = "Modelo Genérico";
|
|||
|
|
|
|||
|
|
var existing = await db.QueryFirstOrDefaultAsync<int?>(
|
|||
|
|
"SELECT ModelID FROM Models WHERE BrandID = @brandId AND Name = @modelName",
|
|||
|
|
new { brandId, modelName });
|
|||
|
|
|
|||
|
|
if (existing.HasValue) return existing.Value;
|
|||
|
|
|
|||
|
|
var newId = await db.ExecuteScalarAsync<int>(
|
|||
|
|
"INSERT INTO Models (BrandID, Name) VALUES (@brandId, @modelName); SELECT CAST(SCOPE_IDENTITY() as int);",
|
|||
|
|
new { brandId, modelName });
|
|||
|
|
|
|||
|
|
return newId;
|
|||
|
|
}
|
|||
|
|
}
|