I am writing an application and I was wondering if I can make property "Name" unique - for example there cannot be more than one location with the same name
Code snippet:
public class City: MyClass1
{
[MaxLength(255)]
public required string Name { get; set; }
}
And I want to have property Name as unique (there cannot be more than one city with the same name)
How do I do that?
CodePudding user response:
The short answer is: You don't. C# does not work like this.
The long answer is: There maybe is a solution, but it does not cover all use cases.
public class City: MyClass1
{
private static HashSet<string> names;
private string name;
[MaxLength(255)]
public required string Name { get => name; set => TrySetName(value); }
void TrySetName(string newName)
{
if (!names.Contains(newName))
{
names.Add(newName);
names.Remove(name);
name = newName;
}
else
{
// Handle the failure of setting the name somehow
}
}
}
You use a static HashSet<string>
to check for name collisions. The check itself is cheap. However, you have to make sure that you are never setting the private field directly. If you really want to make sure, you could wrap the string into its own little type that essentially contains a string and the HashSet
, making interactions with the field possible.
Note: This is not thread safe. This is not safe to work with, when communicating with something else, like a database.
It just works in a self contained, single threaded environment. If you really want to ensure uniqueness, you have to make sure that all these cases are also ensured. If you are working with a database, you probably want the database to handle that. You have been warned.
CodePudding user response:
It depends on your use case,
- If uniqueness is a Domain concept, then make an aggregate Cities,
public class Cities : IEnumerable<City>
{
private readonly HashSet<City> _cities = new HashSet<City>();
public void Add(City city)
{
_cities.Add(city);
}
public IEnumerator<City> GetEnumerator()
{
return _cities.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
And then override the equality in City class for the Hash matching to work,
public class City : IEquatable<City>
{
public string Name { get; }
public bool Equals(City? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Name == other.Name;
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return obj.GetType() == GetType() && Equals((City) obj);
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
}
But if it is not a Domain constraint,
Then just add a Unique Key in DB. And you can catch constraint violation exception
during inserts.
- If you are using
entity framework
, then here is an example.
public class CityConfiguration : IEntityTypeConfiguration<City>
{
public void Configure(EntityTypeBuilder<City> builder)
{
builder.ToTable("City");
builder.HasIndex(city => city.Name).IsUnique();
}
}