A tangl is a formal relationship between two otherwise unconnected things (classes, members, etc.). It fills gaps not handled by interfaces and inheritance. A tangl prevents run-time errors by keeping types in sync and managing model pollution. To see an example of the type of error a tangl can prevent, go to this page in the wiki.
The name is (very, very loosely) inspired by quantum entanglement where:
each particle of the pair or group cannot be described independently of the state of the others, including when the particles are separated by a large distance. https://en.wikipedia.org/wiki/Quantum_entanglement
- What does this repository do?
- How to install?
- Can I see an example?
- What about attributes?
- Wouldn't it be great if this was baked into the C# language?
- Roadmap
- Go to the wiki for more info
It allows C# developers to tangl class properties.
Tangl is available at nuget.org
For the Tangl attribute, code analyzer and fix provider: https://www.nuget.org/packages/TanglAnalyzer
PM> Install-Package TanglAnalyzer
For just the Tangl attribute: https://www.nuget.org/packages/Tangl
PM> Install-Package Tangl
Here are three sample classes. The first two are domain classes and define a Person and an Address. A person may have multiple addresses.
namespace Example.Domain
{
public class Person
{
public int PersonId { get; set; }
public List<Address> Addresses { get; set; }
// other properties here
}
public class Address
{
public int AddressId { get; set; }
public int PersonId { get; set; }
public string AddressType { get; set; }
// other properties here
}
}
The third class is a model (or data transfer object - DTO, if you prefer) that combines some elements of both classes to be shipped to a UI for display.
namespace Example
{
class PersonViewModel
{
public int PersonId { get; set; }
public Address HomeAddress { get; set; }
// other properties here
}
}
Now, what happens when the database adminstrator changes the key type of the People table's primary key PersonId from an int to a long? The application developer gets a ticket, quickly changes Person.PersonId from an int to a long, compiles successfully and moves on.
And after that, mysterious errors start popping up at various points in the chain because longer numbers cannot be forced into smaller ones.
Tangls solve the problem by explicitly tying PersonViewModel.PersonId to Person.PersonId. If one is changed, warnings are raised and fixes provided.
It is as simple as defining the property with a Tangl attribute, like this:
namespace Example
{
class PersonViewModel
{
[Tangl("Example.Person.PersonId")]
public int PersonId { get; set; }
public Address HomeAddress { get; set; }
// other properties here
}
}
or, to avoid strings
namespace Example
{
class PersonViewModel
{
[Tangl(typeof(Example.Domain.Person), nameof(Person.PersonId))]
public int PersonId { get; set; }
public Address HomeAddress { get; set; }
// other properties here
}
}
This is what Visual Studio looks like when it discovers a broken tangl.
Attributes are tangled by default but can be fully or partially detangled.
Let's add a "Name" property with attributes to our Person class.
namespace Example.Domain
{
public class Person
{
public int PersonId { get; set; }
[MaxLength(100)]
[Required]
public string Name {get; set; }
public List<Address> Addresses { get; set; }
// other properties here
}
...
Now, we'll add the same property to our view model but "forget" to add the MaxLength attributes.
namespace Example
{
class PersonViewModel
{
[Tangl(typeof(Example.Domain.Person), nameof(Person.PersonId))]
public int PersonId { get; set; }
[Tangl(typeof(Example.Domain.Person), nameof(Person.Name))]
[Required]
public string Name { get; set; }
public Address HomeAddress { get; set; }
// other properties here
}
}
Visual Studio detects missing attributes from the tangl:
Let's say that we want our view model to maintain the MaxLength attribute. That should be tangled but the "Required" attribute should not be.
...
[Tangl(typeof(Example.Domain.Person), nameof(Person.Name),
includeAttributes: true, except: "Required")]
[MaxLength(100)]
public string Name { get; set; }
...
or
...
[Tangl(typeof(Example.Domain.Person), nameof(Person.Name),
includeAttributes: false, except: "MaxLength")]
[MaxLength(100)]
public string Name { get; set; }
...
To except multiple attributes, simply separate with commas: "MaxLength,Required,SomeOtherAttribute".
Attribute tangling includes arguments. If we change our view model MaxLength from 100 to 80:
...
[Tangl(typeof(Example.Domain.Person), nameof(Person.Name),
includeAttributes: true, except: "Required")]
[MaxLength(80)]
public string Name { get; set; }
...
... the resulting warning arises:
Probably! Something like this would be pretty intuitive to C# developers:
namespace Example
{
class PersonViewModel
{
public int PersonId { get; set; } : Person.PersonId // <= NOT VALID C# BUT WE CAN DREAM
public Address HomeAddress { get; set; }
// other properties here
}
}
In the meantime, we settle for a tangl attribute.
Possible future additions to this code-base include:
- VB.NET support
- Support for fields and methods
- More robust fix provider
- Update all tangls pointing to changed target
- Detect/insert namespaces
Code contributions are welcome!!!