Skip to main content

Overview

The Municipio (Municipality) entity represents a geographical location in the tournament management system. Each municipality can host multiple teams, establishing the geographical organization of the tournament.

Properties

Id
int
Unique identifier for the municipality.
Nombre
string
required
Name of the municipality.Validation Rules:
  • Required field (cannot be empty)
  • Must contain only letters and spaces
  • Cannot start or end with whitespace
  • Minimum length: 3 characters
  • Maximum length: 50 characters
  • Regex pattern: ^(?!^\s)(?!.*\s$)[a-zA-ZÀ-ÿ\s]+$
Display Name: “Nombre del municipio”Error Messages:
  • Pattern mismatch: “Solo se permiten letras”
  • Required: “El nombre del municipio es obligatorio.”
  • Max length: “El nombre del municipio no puede tener más de 50 caracteres.”
  • Min length: “El nombre del municipio no puede tener menos de 3 caracteres.”
Equipos
List<Equipo>
Collection of teams based in this municipality.Default Value: Empty listRelationship: One-to-Many (One municipality can have zero or multiple teams, but each team belongs to exactly one municipality)Related Entity: Equipo

Relationships

Teams (Equipos)

Equipos
List<Equipo>
A municipality can host multiple teams. This is a one-to-many relationship where the municipality is the parent entity.Navigation Property: EquiposCardinality: One municipality to many teams (0..*)Related Entity: EquipoBusiness Rule: A municipality can have zero or multiple teams, but each team must belong to exactly one municipality.
The relationship comment in the source code explicitly states: “Un municipio puede tener 0 o varios equipos, pero un equipo pertenece a un único municipio” (A municipality can have 0 or multiple teams, but a team belongs to a single municipality).

Validation Attributes

The Municipio entity uses the following Data Annotations:
  • [Required] - Ensures the municipality name is mandatory
  • [RegularExpression] - Validates name contains only letters and spaces with no leading/trailing whitespace
  • [MaxLength] / [MinLength] - Enforces character length constraints
  • [Display] - Provides user-friendly display name
  • [DisplayFormat] - Prevents empty strings from being converted to null

Code Examples

Entity Definition

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Torneo.App.Dominio
{
    public class Municipio
    {
        public int Id { get; set; }

        [RegularExpression(@"^(?!^\s)(?!.*\s$)[a-zA-ZÀ-ÿ\s]+$", 
            ErrorMessage = "Solo se permiten letras")]
        [Display(Name = "Nombre del municipio")]
        [Required(AllowEmptyStrings=false, 
            ErrorMessage = "El nombre del municipio es obligatorio.")]       
        [DisplayFormat(ConvertEmptyStringToNull=false)]
        [MaxLength(50, ErrorMessage = "El nombre del municipio no puede tener más de 50 caracteres.")]
        [MinLength(3, ErrorMessage = "El nombre del municipio no puede tener menos de 3 caracteres.")]
        public string Nombre { get; set; }

        public List<Equipo> Equipos { get; set; } = new List<Equipo>();
    }
}

Creating a Municipality

var municipio = new Municipio
{
    Nombre = "Bogotá"
};

Creating a Municipality with Teams

var municipio = new Municipio
{
    Nombre = "Medellín",
    Equipos = new List<Equipo>
    {
        new Equipo { Nombre = "Atlético Nacional" },
        new Equipo { Nombre = "Deportivo Independiente Medellín" }
    }
};

Valid Name Examples

// Valid municipality names
Nombre = "Bogotá";
Nombre = "Cali";
Nombre = "Santa Marta";
Nombre = "San José del Guaviare";
Nombre = "Cartagena de Indias";

Invalid Name Examples

These examples will fail validation:
// Invalid municipality names
Nombre = "123";              // Contains only numbers
Nombre = "Bogotá123";        // Contains numbers
Nombre = " Cali";            // Starts with whitespace
Nombre = "Cali ";            // Ends with whitespace
Nombre = "AB";               // Less than 3 characters
Nombre = "";                 // Empty (required)
Nombre = "A".PadRight(51);   // More than 50 characters

Querying Municipalities

// Get all municipalities with their teams
var municipiosWithTeams = context.Municipios
    .Include(m => m.Equipos)
    .ToList();

// Get municipalities that have teams
var municipiosConEquipos = context.Municipios
    .Where(m => m.Equipos.Any())
    .ToList();

// Get municipalities with no teams
var municipiosSinEquipos = context.Municipios
    .Where(m => !m.Equipos.Any())
    .ToList();

// Get municipality by name
var bogota = context.Municipios
    .FirstOrDefault(m => m.Nombre == "Bogotá");

Adding Teams to a Municipality

// Add a single team
var equipo = new Equipo { Nombre = "Millonarios FC" };
municipio.Equipos.Add(equipo);

// Add multiple teams
var nuevosEquipos = new List<Equipo>
{
    new Equipo { Nombre = "Santa Fe" },
    new Equipo { Nombre = "La Equidad" }
};
municipio.Equipos.AddRange(nuevosEquipos);

Municipality Statistics

// Count teams per municipality
var teamCounts = context.Municipios
    .Select(m => new 
    { 
        Municipio = m.Nombre, 
        NumeroEquipos = m.Equipos.Count 
    })
    .OrderByDescending(x => x.NumeroEquipos)
    .ToList();

// Get municipalities with most teams
var municipioConMasEquipos = context.Municipios
    .OrderByDescending(m => m.Equipos.Count)
    .FirstOrDefault();

Validation in Practice

// Example: Validate before saving
public bool ValidateMunicipio(Municipio municipio)
{
    var validationContext = new ValidationContext(municipio);
    var validationResults = new List<ValidationResult>();
    
    bool isValid = Validator.TryValidateObject(
        municipio, 
        validationContext, 
        validationResults, 
        validateAllProperties: true
    );
    
    if (!isValid)
    {
        foreach (var validationResult in validationResults)
        {
            Console.WriteLine(validationResult.ErrorMessage);
        }
    }
    
    return isValid;
}

Common Use Cases

Geographic Tournament Organization

// Group matches by municipality
var matchesByMunicipality = context.Partidos
    .Include(p => p.Local)
        .ThenInclude(e => e.Municipio)
    .GroupBy(p => p.Local.Municipio.Nombre)
    .Select(g => new 
    { 
        Municipio = g.Key, 
        TotalMatches = g.Count() 
    });

Regional Leagues

// Get all teams from a specific region
var teamsByRegion = context.Municipios
    .Where(m => m.Nombre.StartsWith("San"))
    .SelectMany(m => m.Equipos)
    .ToList();

Tournament Coverage

// Calculate tournament geographical coverage
var coverage = new
{
    TotalMunicipalities = context.Municipios.Count(),
    MunicipalitiesWithTeams = context.Municipios.Count(m => m.Equipos.Any()),
    TotalTeams = context.Equipos.Count()
};

Database Mapping

The Municipio entity is mapped to a database table with the same name. The Id property serves as the primary key and is auto-generated.The relationship with Equipo is established through a foreign key in the Equipo table pointing to the Municipio’s Id.

Business Rules

Zero-to-Many Relationship

The municipality-team relationship supports the following scenarios:
  1. New Municipality: A municipality can be created without any teams
  2. Active Municipality: A municipality with one or more teams
  3. Empty Municipality: A municipality that had teams but currently has none (teams moved or dissolved)

Data Integrity

When deleting a municipality, consider the following:
  • Determine whether to cascade delete teams or prevent deletion if teams exist
  • Implement business logic to handle orphaned teams
  • Consider soft deletes for historical data preservation
  • Equipo - Teams based in this municipality
  • DirectorTecnico - Technical directors of teams (indirect)
  • Jugador - Players on teams in this municipality (indirect)
  • Partido - Matches involving teams from this municipality (indirect)

Build docs developers (and LLMs) love