Default parameter values in primary constructor

I've created a data model class for use with Entity Framework Core, and I just had a question regarding my use of a primary constructor here. As you can see, IsActive and Prizes are both being initialized to default values. Will this give me the same behaviour as if I'd used an old-school constructor? (i.e. public RaffleModel(...)... you know the drill) Additionally, are there any considerations I need to have specifically relating to how Entity Framework Core will interact with this class?
public class RaffleModel
(string title, string description, DateTime drawDate, bool isBundle, string raffleHolderId)
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int RaffleId { get; set; } // auto-generated

[Required]
[StringLength(30)]
public string Title { get; set; } = title;

[Required]
[StringLength(500)]
public string Description { get; set; } = description;

[Required]
public DateTime DrawDate { get; set; } = drawDate;

// Set to true when the raffle is published (after prizes are added)
[Required]
public bool IsActive { get; set; } = false;

[Required]
public bool IsBundle { get; set; } = isBundle;

[ForeignKey("AspNetUsers")]
public string RaffleHolderId { get; set; } = raffleHolderId;

// Identity user reference
public virtual ApplicationUser? RaffleHolder { get; set; }

// Navigation property (initialised to prevent null references)
public virtual ICollection<PrizeModel> Prizes { get; set; } = new HashSet<PrizeModel>();
}
public class RaffleModel
(string title, string description, DateTime drawDate, bool isBundle, string raffleHolderId)
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int RaffleId { get; set; } // auto-generated

[Required]
[StringLength(30)]
public string Title { get; set; } = title;

[Required]
[StringLength(500)]
public string Description { get; set; } = description;

[Required]
public DateTime DrawDate { get; set; } = drawDate;

// Set to true when the raffle is published (after prizes are added)
[Required]
public bool IsActive { get; set; } = false;

[Required]
public bool IsBundle { get; set; } = isBundle;

[ForeignKey("AspNetUsers")]
public string RaffleHolderId { get; set; } = raffleHolderId;

// Identity user reference
public virtual ApplicationUser? RaffleHolder { get; set; }

// Navigation property (initialised to prevent null references)
public virtual ICollection<PrizeModel> Prizes { get; set; } = new HashSet<PrizeModel>();
}
28 Replies
Angius
Angius5mo ago
Just don't use the constructor and make the necessary properties required That's my approach, at least (get rid of the virtual while you're at it) That said, here's what your code compiles to:
MODiX
MODiX5mo ago
Angius
sharplab.io (click here)
public class RaffleModel
(string title, string description, DateTime drawDate, ... {
public int RaffleId { get; set; } // auto-generated
public string Title { get; set; } = title;
public string Description { get; set; } = description;
public DateTime DrawDate { get; set; } = drawDate;
public bool IsBundle { get; set; } = isBundle;
public string RaffleHolderId { get; set; } = raffleHol...
}
public class RaffleModel
(string title, string description, DateTime drawDate, ... {
public int RaffleId { get; set; } // auto-generated
public string Title { get; set; } = title;
public string Description { get; set; } = description;
public DateTime DrawDate { get; set; } = drawDate;
public bool IsBundle { get; set; } = isBundle;
public string RaffleHolderId { get; set; } = raffleHol...
}
Try the /sharplab command! | React with ❌ to remove this embed.
Angius
Angius5mo ago
Generates a ctor that directly assigns the values to the backing fields of your properties
Maverick (Shaun)
As in, have no constructor at all? Is that good practice? Also, in some other models I may have fields that are nullable and not required, but still may be initialized upon instance creation - as well as having calculated values assigned upon instance creation. Would that throw a spanner in the works? I've used the virtual keyword to opt for lazy-loading rather than eager-loading, from what I can see, that's pretty common practice no? That's actually the first time I've seen compiled C# code (I'm still a newbie compared to most on here probably) 😄
Angius
Angius5mo ago
Yes, as in no constructor at all. Making a property required (the C# keyword, not the [Required] attribute) makes it so that it is... required, during construction
public class Foo
{
public required string Name { get; set; }
public required int Count { get; set; }
}

var foo = new Foo {
Name = "Green Chair",
Count = 69,
}

var fooErr = new Foo {
Name = "Red Chair", // error
}
public class Foo
{
public required string Name { get; set; }
public required int Count { get; set; }
}

var foo = new Foo {
Name = "Green Chair",
Count = 69,
}

var fooErr = new Foo {
Name = "Red Chair", // error
}
Maverick (Shaun)
Also, this may be implied by the compiled code you sent (I'm not sure) - but is what I currently have valid or invalid?
Angius
Angius5mo ago
It is valid, yes Regarding lazy loading... no. Save yourself unnecessary database trips and load things eagerly Load them eagerly, and load them in limited capacity with .Select() The only times I see lazy loading being used is: 1. My professor is 97 years old and told us that's how it was done in Visual Basic days 2. This is my first C# project ever I started learning yesterday from this musty 2001 book
Maverick (Shaun)
To be honest, I need to balance efficiency with ease of development/use. This is for a university project and they're not teaching any of the stack that I've chosen to use. I opted for lazy loading thinking that it would be easier to implement and require less code. With that in mind, would you still recommend eager loading?
Angius
Angius5mo ago
It would be easier, yes, and it would require less code, yes But it will produce worse and less performant code Whether whoever's gonna be grading it will pay attention to that, dunno
Maverick (Shaun)
Honestly, I'm not sure they will - the only part of the grading grid that directly references the code is: "Code is elegant, suitably complex but navigable: it is exceptionally easy to tie up elements on the website to the code associated with them." So with lazy-loading being a valid approach, although less performant, I think it should be totally fine. If it makes my life easier on a project like this with not a whole lot of time to work on, it's probably the best bet
Angius
Angius5mo ago
Sure, as long as you remember to never use it in real life
Maverick (Shaun)
Side note - until a few days ago, I'd only ever used C# for game development and this project has already highlighted just how little of the language you even need to touch for the majority of game dev (especially small projects) Yep! I'm sure if I ever end up using it in a professional capacity, those sorts of things will probably be enforced anyway. Unfortunately, I only get to use C in a professional capacity at the moment aaaa
Angius
Angius5mo ago
A bit of explanation:
var thing = await _context.Things
.Where(t => t.Id == id)
.Select(t => new ThingDto {
Name = t.Name,
Categories = t.Categories.Select(c => c.Name)
})
.FirstOrDefaultAsync();
var thing = await _context.Things
.Where(t => t.Id == id)
.Select(t => new ThingDto {
Name = t.Name,
Categories = t.Categories.Select(c => c.Name)
})
.FirstOrDefaultAsync();
will query the database with something like
SELECT t.Name, c.Name FROM Things t
WHERE t.Id = @id
JOIN Categories c ON t.Id = c.ThingId
LIMIT 1;
SELECT t.Name, c.Name FROM Things t
WHERE t.Id = @id
JOIN Categories c ON t.Id = c.ThingId
LIMIT 1;
while
var thing = await _context.Things
.Where(t => t.Id == id)
.FirstOrDefaultAsync();
var thing = await _context.Things
.Where(t => t.Id == id)
.FirstOrDefaultAsync();
will call
SELECT * FROM Things t
WHERE t.Id = @id
LIMIT 1;
SELECT * FROM Things t
WHERE t.Id = @id
LIMIT 1;
and then referencing the virtual property
var cats = thing.Categories;
var cats = thing.Categories;
will run a second query,
SELECT * FROM Categories c
WHERE c.ThingId = @id
SELECT * FROM Categories c
WHERE c.ThingId = @id
You end up with two database calls instead of one, and with more data fetched
Maverick (Shaun)
Thanks for the explanation! It's 4am here so I'll look at it again after a snooze when my eyes aren't half shut, but I see your point and can see why it'd be bad practice in the real world. Ordinarily I'd always prefer to do things the best way, but tight deadlines means corner cutting has to happen somewhere unfortunately lol
Angius
Angius5mo ago
Sure thing
Pobiega
Pobiega5mo ago
regarding constructors, if you want to have a ctor for "human" use that validates domain rules for example, you can create a second private constructor for EFs use.
i like chatgpt
i like chatgpt5mo ago
EF Core always needs to call a constructor. Case 1 (Default Parameterless Constructor)
class Auto_Generated_Parameterless_Constructor
{
public required int Id { get; init; }
public required string Name { get; set; } = default!;
public int Age { get; set; }
}
class Auto_Generated_Parameterless_Constructor
{
public required int Id { get; init; }
public required string Name { get; set; } = default!;
public int Age { get; set; }
}
EF Core - calls this default parameter constructor first. - and then populates the properties. Case 2 (Non Default Parameterless Constructor)
class Manually_Added_Parameterless_Constructor
{
public required int Id { get; init; }
public required string Name { get; set; } = default!;
public int Age { get; set; }
// usually private or protected
private Manually_Added_Parameterless_Constructor() { }
}
class Manually_Added_Parameterless_Constructor
{
public required int Id { get; init; }
public required string Name { get; set; } = default!;
public int Age { get; set; }
// usually private or protected
private Manually_Added_Parameterless_Constructor() { }
}
EF Core - calls this non default parameter constructor first. - and then populates the properties. We usually do this to restrict outsiders from instantiate this class. Case 3 (Matching Parameter Constructor)
class Matching_Parameter_Constructor(int age)
{
public required int Id { get; init; }
public required string Name { get; set; } = default!;
public int Age { get; set; } = age;
}
class Matching_Parameter_Constructor(int age)
{
public required int Id { get; init; }
public required string Name { get; set; } = default!;
public int Age { get; set; } = age;
}
EF Core - calls this matching parameter constructor first. - and then populates the remaining properties. NOTES: If there is more than one matching constructor, EF Core selects one of them with its own algorithm. I don't know the details of this algorithm to be honest. Case 4 (Non Matching Parameter Constructor) The following will throw exceptions because EF Core cannot instantiate this class.
class Non_Matching_Parameter_Constructor_1(int aGe)
{
public required int Id { get; init; }
public required string Name { get; set; } = default!;
public int Age { get; set; } = aGe;
}
class Non_Matching_Parameter_Constructor_2(int age, string fullName)
{
public required int Id { get; init; }
public required string Name { get; set; } = fullName;
public int Age { get; set; } = age;
}
class Non_Matching_Parameter_Constructor_3(int age, string name, bool isMarried)
{
public required int Id { get; init; }
public required string Name { get; set; } = isMarried? $"Mr(s). {name}": name;
public int Age { get; set; } = age;
}
class Non_Matching_Parameter_Constructor_1(int aGe)
{
public required int Id { get; init; }
public required string Name { get; set; } = default!;
public int Age { get; set; } = aGe;
}
class Non_Matching_Parameter_Constructor_2(int age, string fullName)
{
public required int Id { get; init; }
public required string Name { get; set; } = fullName;
public int Age { get; set; } = age;
}
class Non_Matching_Parameter_Constructor_3(int age, string name, bool isMarried)
{
public required int Id { get; init; }
public required string Name { get; set; } = isMarried? $"Mr(s). {name}": name;
public int Age { get; set; } = age;
}
So we need to add non default parameterless constructor. Assuming you have added the non default parameterless constructor, EF Core - calls the non default parameterless constructor first. - and then populates the properties.
Maverick (Shaun)
Just to be clear, are you saying it's essential to define a non-default parameterless constructor? On my little test project I was using before moving over to this, I had a simple model class shown below:
public class RaffleModel(string name, string description, bool isBundle)
{
[Key] public int Id { get; set; }
[Required] public string Name { get; set; } = name;
[Required] public string Description { get; set; } = description;
[Required] public bool IsBundle { get; set; } = isBundle;
}
public class RaffleModel(string name, string description, bool isBundle)
{
[Key] public int Id { get; set; }
[Required] public string Name { get; set; } = name;
[Required] public string Description { get; set; } = description;
[Required] public bool IsBundle { get; set; } = isBundle;
}
DbSet created in DB context:
public DbSet<RaffleModel> Raffle { get; set; }
public DbSet<RaffleModel> Raffle { get; set; }
Raffle service for CRUD operations, injected where relevant:
public class RaffleService(MyDbContext context) : IRaffleService
{

/* CREATE METHODS */

public async Task AddNewRaffle(RaffleModel newRaffle)
{
context.Raffle.Add(newRaffle);
await context.SaveChangesAsync();
}

/* READ METHODS */

public async Task<List<RaffleModel>?> GetAllRaffles()
{
return await context.Raffle.ToListAsync();
}
}
public class RaffleService(MyDbContext context) : IRaffleService
{

/* CREATE METHODS */

public async Task AddNewRaffle(RaffleModel newRaffle)
{
context.Raffle.Add(newRaffle);
await context.SaveChangesAsync();
}

/* READ METHODS */

public async Task<List<RaffleModel>?> GetAllRaffles()
{
return await context.Raffle.ToListAsync();
}
}
New raffle entry created with:
var newRaffle = new RaffleModel(_title, _description, _isBundle);
await RaffleService.AddNewRaffle(newRaffle);
var newRaffle = new RaffleModel(_title, _description, _isBundle);
await RaffleService.AddNewRaffle(newRaffle);
This was creating records in the database which I was also able to later retrieve. As far as I can tell, there were no issues with this, even though I did not define a parameterless constructor. Unless I have misunderstood you, aren't you saying that the above scenario would not work? If so, is there maybe some sort of issue that I haven't yet noticed, even though everything looked to be working okay? Thanks for the detailed response, sorry if I've misunderstood it.
i like chatgpt
i like chatgpt5mo ago
If possible always make the model class the dumbest (aka simplest) one. Your current model below is not the dumbest because you have defined a matching parameter constructor. Yours belongs to case 3.
public class RaffleModel(string name, string description, bool isBundle)
{
[Key] public int Id { get; set; }
[Required] public string Name { get; set; } = name;
[Required] public string Description { get; set; } = description;
[Required] public bool IsBundle { get; set; } = isBundle;
}
public class RaffleModel(string name, string description, bool isBundle)
{
[Key] public int Id { get; set; }
[Required] public string Name { get; set; } = name;
[Required] public string Description { get; set; } = description;
[Required] public bool IsBundle { get; set; } = isBundle;
}
As you have a matching parameter constructor you don't need to specify non-default parameterless constructor. To make your class the dumbest one, remove the parameter constructor as follows.
public class RaffleModel
{
// Id should be immutable for the whole life of the instance. You can choose either private init or init.
// If you want to be able to initialize this Id outside this class, make it init. But your Id is of type int, so you might NOT want to initialize it from outside.
// Otherwise make it private so only EF Core can initialize it.
[Key] public int Id { get; private init; }
[Required] public string Name { get; set; }
[Required] public string Description { get; set; }
[Required] public bool IsBundle { get; set; }
}
public class RaffleModel
{
// Id should be immutable for the whole life of the instance. You can choose either private init or init.
// If you want to be able to initialize this Id outside this class, make it init. But your Id is of type int, so you might NOT want to initialize it from outside.
// Otherwise make it private so only EF Core can initialize it.
[Key] public int Id { get; private init; }
[Required] public string Name { get; set; }
[Required] public string Description { get; set; }
[Required] public bool IsBundle { get; set; }
}
Now you can create the instance outside the class as
var newRaffle = new RaffleModel{Name = _title, Description= _description, IsBundle = _isBundle};
await RaffleService.AddNewRaffle(newRaffle);
var newRaffle = new RaffleModel{Name = _title, Description= _description, IsBundle = _isBundle};
await RaffleService.AddNewRaffle(newRaffle);
Just another case for the sake of completeness.
public class RaffleModel
{
[Key] public Guid Id { get; init; } // <=== public init and Guid type
[Required] public string Name { get; set; }
[Required] public string Description { get; set; }
[Required] public bool IsBundle { get; set; }
}
public class RaffleModel
{
[Key] public Guid Id { get; init; } // <=== public init and Guid type
[Required] public string Name { get; set; }
[Required] public string Description { get; set; }
[Required] public bool IsBundle { get; set; }
}
When Id is public init and of type Guid, you can instantiate the class with Id too as follows.
var id = Guid.NewGuid();
var newRaffle = new RaffleModel{Id = id, Name = _title, Description= _description, IsBundle = _isBundle};
await RaffleService.AddNewRaffle(newRaffle);

// use id to create other entity instance, for example, Order as follows.

var order = new Order {RaffleId = id, blablabla = ..., etc = ...};
await OrderService.AddNewOrder(order);
var id = Guid.NewGuid();
var newRaffle = new RaffleModel{Id = id, Name = _title, Description= _description, IsBundle = _isBundle};
await RaffleService.AddNewRaffle(newRaffle);

// use id to create other entity instance, for example, Order as follows.

var order = new Order {RaffleId = id, blablabla = ..., etc = ...};
await OrderService.AddNewOrder(order);
We need non-default parameterless constructor for one of the following cases: - Case 2: our model has no parameter constructor and we want to prevent the model class from being instantiated from outside the class. - Case 4: our model has no matching parameter constructor.
Maverick (Shaun)
Okay, I see what you're saying - I assume the reasoning behind this approach is centred around flexibility and alignment with EF Core principles? Additionally, it sounds like it's preferable to separate the business logic from the entity model? I've had a go at taking on board what you've said and applied it one of my entity models. Message length limits so I'll send them in separate messages. OLD MODEL:
public class EntryModel
{
/// <summary>
/// Represents an entry in a raffle, which can be made by registered users or guests.
/// </summary>
/// <param name="raffleId">The identifier of the associated raffle.</param>
/// <param name="userId">(Nullable) The identifier of the user, if the entry is made by a registered user.</param>
/// <param name="isGuest">Indicates whether the entry is made by a guest.</param>
/// <param name="guestEmail">(Nullable) The email address of the guest, if the entry is made by a guest.</param>
public EntryModel(int raffleId, string? userId, bool isGuest, string? guestEmail)
{
RaffleId = raffleId;
UserId = userId;
IsGuest = isGuest;

// Properties below are only relevant for guests
if (!IsGuest) return;

GuestEmail = guestEmail;
// Randomly generated 32 character string
GuestReferenceCode = Guid.NewGuid().ToString("N");
}

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int EntryId { get; set; } // auto-generated

[ForeignKey("RaffleModel")]
public int RaffleId { get; set; }

// Nullable for guest entries
[ForeignKey("AspNetUsers")]
public string? UserId { get; set; }

[Required]
public bool IsGuest { get; set; }

// Nullable, used if IsGuest is true
[EmailAddress]
[StringLength(255)]
public string? GuestEmail { get; set; }

// Nullable, unique reference for guest entries, used if IsGuest is true
[StringLength(32)]
public string? GuestReferenceCode { get; set; }

// Identity user reference
public virtual ApplicationUser? User { get; set; }

// Raffle reference
public virtual RaffleModel? Raffle { get; set; }
}
public class EntryModel
{
/// <summary>
/// Represents an entry in a raffle, which can be made by registered users or guests.
/// </summary>
/// <param name="raffleId">The identifier of the associated raffle.</param>
/// <param name="userId">(Nullable) The identifier of the user, if the entry is made by a registered user.</param>
/// <param name="isGuest">Indicates whether the entry is made by a guest.</param>
/// <param name="guestEmail">(Nullable) The email address of the guest, if the entry is made by a guest.</param>
public EntryModel(int raffleId, string? userId, bool isGuest, string? guestEmail)
{
RaffleId = raffleId;
UserId = userId;
IsGuest = isGuest;

// Properties below are only relevant for guests
if (!IsGuest) return;

GuestEmail = guestEmail;
// Randomly generated 32 character string
GuestReferenceCode = Guid.NewGuid().ToString("N");
}

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int EntryId { get; set; } // auto-generated

[ForeignKey("RaffleModel")]
public int RaffleId { get; set; }

// Nullable for guest entries
[ForeignKey("AspNetUsers")]
public string? UserId { get; set; }

[Required]
public bool IsGuest { get; set; }

// Nullable, used if IsGuest is true
[EmailAddress]
[StringLength(255)]
public string? GuestEmail { get; set; }

// Nullable, unique reference for guest entries, used if IsGuest is true
[StringLength(32)]
public string? GuestReferenceCode { get; set; }

// Identity user reference
public virtual ApplicationUser? User { get; set; }

// Raffle reference
public virtual RaffleModel? Raffle { get; set; }
}
NEW MODEL:
public class EntryModel
{
/// <summary>
/// Auto-generated primary key for the entry.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int EntryId { get; private init; }

/// <summary>
/// The identifier of the associated raffle.
/// </summary>
[ForeignKey("RaffleModel")]
public int RaffleId { get; init; }

/// <summary>
/// (Nullable) The identifier of the user, if the entry is made by a registered user.
/// </summary>
[ForeignKey("AspNetUsers")]
public string? UserId { get; set; }

/// <summary>
/// Indicates whether the entry is made by a guest.
/// </summary>
[Required]
public bool IsGuest { get; set; }

/// <summary>
/// (Nullable) The email address of the guest, if the entry is made by a guest.
/// </summary>
[EmailAddress]
[StringLength(255)]
public string? GuestEmail { get; set; }

/// <summary>
/// (Nullable) A unique reference for guest entries, if the entry is made by a guest.
/// </summary>
[StringLength(32)]
public string? GuestReferenceCode { get; set; }

/// <summary>
/// Navigation property to the associated ApplicationUser (if any).
/// </summary>
public virtual ApplicationUser? User { get; set; }

/// <summary>
/// Navigation property to the associated Raffle.
/// </summary>
public virtual RaffleModel? Raffle { get; set; }
}
public class EntryModel
{
/// <summary>
/// Auto-generated primary key for the entry.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int EntryId { get; private init; }

/// <summary>
/// The identifier of the associated raffle.
/// </summary>
[ForeignKey("RaffleModel")]
public int RaffleId { get; init; }

/// <summary>
/// (Nullable) The identifier of the user, if the entry is made by a registered user.
/// </summary>
[ForeignKey("AspNetUsers")]
public string? UserId { get; set; }

/// <summary>
/// Indicates whether the entry is made by a guest.
/// </summary>
[Required]
public bool IsGuest { get; set; }

/// <summary>
/// (Nullable) The email address of the guest, if the entry is made by a guest.
/// </summary>
[EmailAddress]
[StringLength(255)]
public string? GuestEmail { get; set; }

/// <summary>
/// (Nullable) A unique reference for guest entries, if the entry is made by a guest.
/// </summary>
[StringLength(32)]
public string? GuestReferenceCode { get; set; }

/// <summary>
/// Navigation property to the associated ApplicationUser (if any).
/// </summary>
public virtual ApplicationUser? User { get; set; }

/// <summary>
/// Navigation property to the associated Raffle.
/// </summary>
public virtual RaffleModel? Raffle { get; set; }
}
As per your advice, I've simplified the entity model and separated the business logic. I've made EntryId private init, and RaffleId init. If I've understood you correctly, I believe this is a good change. If I'm on the right track, I have a follow up question - is it better practice to directly create an EntryModel object in the relevant part of the code (i.e. in my Blazor application, create it in the code-behind the relevant page where an entry is made), or create an intermediate class that has functions like CreateUserEntry(), CreateGuestEntry() that act as sort of separated constructors for this entity model? Again, thank you for the assistance - this is the sort of detail that we really don't have time to go into at university Sorry, forgot to tag you in my response
Angius
Angius5mo ago
(Nullable)
Yes, the type has a question mark, it is nullable. Kinda redundant
EntryId
The Entry prefix is redundant as well In general, it's best that the database models don't leave the app. I'm not 100% sure how Blazor does it, but I would guess anything you bind to would be "leaving the app boundary", so I'd introduce DTOs there at least
Maverick (Shaun)
Meh, stylistic choice - some argue it's pointless, some argue it's good practice. I'm personally somewhere in the middle but opted to use the table name prefix for this project Okay - I'll have a look into it
Angius
Angius5mo ago
Some people argue generic repositories on top of EF are a good idea I believe it's a good idea to not listen to stupid people
Maverick (Shaun)
Agreed, but having a stylistic preference over whether to use the table name prefix for an ID is not a case where either side is stupid. Both are perfectly valid, different SQL naming conventions use either option The company I work for enforces the use of the table name prefix for the primary key as a standard
Angius
Angius5mo ago
Sure it's a standard, but it's a stupid standard Useless code for the sake of useless code
Maverick (Shaun)
That's your opinion, there are valid arguments for both options
Angius
Angius5mo ago
I don't see any arguments for Model.ModelNameForsureItBelongsToTheModelAndItsANameAlsoItsVarcharOneHundred But sure, agree to disagree
Maverick (Shaun)
Sure, if it ends up being that, that's probably rather stupid. However, using a unrealistic and ridiculously convoluted example is not a valid argument