System.StackOverflowException on BlogEngine.NET - c#

I am using BlogEngine.NET and need to set the BlogId to a specific Guid (blogIdstr). I can't seem to figure out how to change it from the default blogId. This is the code I have at the moment but it is giving me a StackOverflowException...
These two are in the base class...
public virtual TKey Id { get; set; }
public Guid BlogId
{
get
{
return BlogId; <-- Stack Overflow
}
set
{
string blogIdstr = "FCA96EFB-D51C-4C41-9F85-3EEB9C50BDE7";
Guid blogIdGuid = Guid.Empty;
blogIdGuid = Guid.Parse(blogIdstr);
}
}
And this one is in blog.cs...
public override Guid Id
{
get { return base.Id; }
set
{
base.Id = value;
base.BlogId = value;
}
}
How can I set the blogId and avoid the StackOverflowException? Thanks in advance.

For the first one, in BlogId, you're returning BlogId, which fires the Getter that returns... BlogId. Boom, stack overflow. Return blogIdGuid in your public getter instead of BlogId.
I'm guessing the second one is related to the first, but without more code I can't tell offhand.
Edit: Whoops, misread the code. Yeah, use a backing class-level property called _blogId and set that in the setter and return it in the getter.

You just need to introduce a backing variable
private Guid _blogId; and be sure to set that field in your set method
public Guid BlogId
{
get
{
return _blogId;
}
set
{
string blogIdstr = "FCA96EFB-D51C-4C41-9F85-3EEB9C50BDE7";
Guid blogIdGuid = Guid.Empty;
blogIdGuid = Guid.Parse(blogIdstr);
_blogId = value;
}
}

Your get method is calling itself, and your set method is essentially issuing a no-op by setting a value local to the method. You need a backing field for your property if you want to do something inside of the getter and setter:
private Guid _blogId;
public Guid BlogId
{
get
{
return _blogId;
}
set
{
//some operation against value here, Validate(value), etc.
_blogId = value;
}
}
If you have no action to take in the getter/setter, you can use an auto property, which will generate the backing field for you:
public Guid BlogId { get; set; }
What you can't do, and what it appears you're really trying to do here, is pass a different type into a property - to do that you'd need a method on the class, i.e.:
public bool TrySetBlogId(string newId)
{
Guid id;
var stringIsGuid = Guid.TryParse(newId, out id);
if (stringIsGuid)
{
BlogId = id;
}
return stringIsGuid;
}

Related

DDD modelling of articles, variants and their parameters

I am learning DDD and trying to model articles, its variants and parameters.
Article can be on it's own without variants
Variant must be child of an article
both article and variant can have some parameters (colors, brands, sizes...), physical quantities (width, length, some article-specific like inner length)
If you set some parameter on an article, it can be "synchronized" to it's children variants
you can override this in a variant by setting that parameter as "unlinked", then this variant would have different parameter value than article
some parameters can be set multiple times (color: red, blue), but some only once (brand)
those parameters are dynamically create, it's not a Color or Brand property but key-value selected from preconfigured values
I think my main aggregate roots will be Article and Variant.
My current code looks like this:
internal class Article : AggregateRoot<ArticleId>
{
private readonly ISet<VariantId> _variants = new HashSet<VariantId>();
private readonly ISet<AssignedParameter> _parameters = new HashSet<AssignedParameter>();
private readonly ISet<AssignedPhysicalQuantity> _physicalQuantities = new HashSet<AssignedPhysicalQuantity>();
public string Name { get; private set; }
public string Catalog { get; private set; }
public IReadOnlySet<VariantId> Variants => _variants.AsReadOnly();
public IReadOnlySet<AssignedParameter> Parameters => _parameters.AsReadOnly();
public IReadOnlySet<AssignedPhysicalQuantity> PhysicalQuantities => _physicalQuantities.AsReadOnly();
private Article(ArticleId id, string name, string catalog)
: base(id)
{
Name = name;
Catalog = catalog;
}
public static Article Register(ArticleId id, string name, string catalog)
{
var article = new Article(id, name, catalog);
article.AddEvent(new ArticleRegistered(article.Id, article.Name, article.Catalog));
return article;
}
public void AssignParameter(Parameter parameter, ParameterValue parameterValue, bool syncToVariants)
{
if (!parameter.CanBeAssignedMultipleTimes && _parameters.Any(p => p.ParameterId == parameter.Id))
{
throw new ParameterCanBeAssignedOnlyOnceException($"Parameter {parameter.Id} can by assigned only once.");
}
var assignedParameter = new AssignedParameter(parameter.Id, parameterValue.Id, syncToVariants);
if (!_parameters.Add(assignedParameter))
{
throw new ParameterIsAlreadyAssignedException($"Parameter {parameter.Id} with value {parameterValue.Id} is already assigned.");
}
AddEvent(new ArticleParameterAssigned(Id, assignedParameter.ParameterId, assignedParameter.ParameterValueId));
}
public void UnassignParameter(Parameter parameter, ParameterValue parameterValue)
{
var assignedParameter = _parameters.FirstOrDefault(p => p.ParameterId == parameter.Id && p.ParameterValueId == parameterValue.Id);
if (assignedParameter is null)
{
throw new ParameterIsNotAssignedException($"Parameter {parameter.Id} is not assigned.");
}
_parameters.Remove(assignedParameter);
AddEvent(new ArticleParameterUnassigned(Id, assignedParameter.ParameterId, assignedParameter.ParameterValueId));
}
// physical quantity assign / unassign are similar to parameters
}
internal class Variant : AggregateRoot<VariantId>
{
private readonly ISet<AssignedParameter> _parameters = new HashSet<AssignedParameter>();
private readonly ISet<AssignedPhysicalQuantity> _physicalQuantities = new HashSet<AssignedPhysicalQuantity>();
public string Name { get; private set; }
public string Catalog { get; private set; }
public EanCode Ean { get; private set; }
public decimal Weight { get; private set; }
public IReadOnlySet<AssignedParameter> Parameters => _parameters.AsReadOnly();
public IReadOnlySet<AssignedPhysicalQuantity> PhysicalQuantities => _physicalQuantities.AsReadOnly();
internal Variant(VariantId id, string name, string catalog, EanCode ean, decimal weight)
: base(id)
{
Name = name;
Catalog = catalog;
Ean = ean;
Weight = weight;
}
// parameter and physical quantity assignment methods
}
Parameters:
internal class Parameter : AggregateRoot<ParameterId>
{
private readonly ISet<ParameterValue> _values = new HashSet<ParameterValue>();
public string Code { get; private set; }
public string Name { get; private set; }
public bool CanBeAssignedMultipleTimes { get; private set; }
public IReadOnlySet<ParameterValue> Values => _values.AsReadOnly();
public Parameter(ParameterId id, string code, string name, bool canBeAssignedMultipleTimes)
: base(id)
{
Code = code;
Name = name;
CanBeAssignedMultipleTimes = canBeAssignedMultipleTimes;
}
}
internal class ParameterValue : Entity<ParameterValueId>
{
public string Code { get; private set; }
public string Name { get; private set; }
public Parameter Parameter { get; private init; } = null!;
public ParameterValue(ParameterValueId id, string code, string name)
: base(id)
{
Code = code;
Name = name;
}
}
Value objects:
// for Article, variant doesn't have SyncToVariants property and has some other
internal class AssignedParameter : ValueObject
{
public ParameterId ParameterId { get; private init; }
public ParameterValueId ParameterValueId { get; private init; }
public bool SyncToVariants { get; private init; }
public AssignedParameter(ParameterId parameterId, ParameterValueId parameterValueId, bool syncToVariants)
{
ParameterId = parameterId;
ParameterValueId = parameterValueId;
SyncToVariants = syncToVariants;
}
protected override IEnumerable<object> GetEqualityComponents()
{
yield return ParameterId;
yield return ParameterValueId;
}
}
internal class AssignedPhysicalQuantity : ValueObject { ... }
My questions:
What would be the best way to notify variants of the parameter change? I can think of two ways using events.
First would be using ArticleParameterChanged(ArticleId, parameter.Id, parameterValue.Id). I would handle this event and changed all variants at once in the handler - I don't think this is the way, but I wouldn't need to hold variants collection in article.
Second would be to loop through variant IDs and create ArticleVariantParameterChanged(ArticleId, VariantId, parameterId, parameterValueId) event. This seems more correct to me?
if (syncToVariants)
{
foreach (var variantId in _variants)
{
AddEvent(new ArticleVariantParameterChanged(Id, variantId, parameter.Id, parameterValue.Id);
}
}
How do I add new variant to article? The easiest way would be to create new variant and update the article in one transaction.
// Article method
public Variant RegisterVariant(VariantId variantId, ...)
{
var variant = new Variant(variantId, ...);
_variants.Add(variantId);
return variant;
}
// command handler? or domain service?
var article = await _articleRepo.GetAsync(articleId);
var variant = article.RegisterVariant(variantId, ...);
await _variantRepo.AddAsync(variant);
await _articleRepo.UpdateAsync(article);
Or using events?
// Article method
public Variant RegisterVariant(VariantId variantId, ...)
{
var variant = Variant.Register(variantId, this.Id, ...);
return variant;
}
// Variant static method
public Variant Register(VariantId variantId, ArticleId articleId, ...)
{
var variant = new Variant(variantId, articleId, ...);
variant.AddEvent(new VariantRegistered(variantId, articleId));
return variant;
}
// command handler
var variant = article.RegisterVariant(...);
await _variantRepo.AddAsync(variant);
// VariantRegisteredHandler
article.AddVariant(variantId);
However here it seems kind of confusing to me, article.RegisterVariant and article.AddVariant... Maybe it's just wrong naming?
Also here can occur condition race between adding new variant and assigning a new parameter, when someone adds new parameter before the VariantRegistered event was handled, so it wouldn't sync that parameter.
So I'm thinking, is it even good idea to store those shared parameters in each variant? Maybe it would be enough to just have variant specific parameters there and merge everything in the read model? However this would be harder to prevent duplications - if the article already has a parameter "color - red", assigning "color - red" to variant would need to check the article parameters too and there can be another race condition.
I read that entities without any domain business logic could be treated as CRUD, that means they wouldn't even inherit AggregateRoot and each of them would have own repository, right?
Let's say someone really wants to delete some parameter value, for example blue color. This wouldn't (hopefully) happen in my app, but I'm still curious how this would be handled. He confirms he really wants to delete it and I need to go through all articles and unassign it from them. How?
My idea would be either to have ParameterValueDeleted event and ParameterValueDeletedHandler would query for all articles and variants and unassign it one by one, this handler would take really long time to execute.
Or ParameterValueDeletedHandler would query for all IDs, create some event for them and that handler would unassign it later. However in the latter case I don't know how that event would be named to make sense. UnassignArticleParameter seems more like command than event and ArticleParameterUnassigned is something coming from article. Also I read that commands indicate something that can be rejected, so I would say command doesn't fit here.
Also I see a problem when someone deletes that parameter and someone else queries for an article which doesn't have it unassigned yet - database join would fail because it would join to non existent parameter (considering single database for read and write model).
If I wanted to have mandatory parameters, where would be the best place to validate that all of them are set? Move the article registration logic to ArticleFactory and check it there? And for variants maybe ArticleService or VariantFactory? This seems kinda inconsistent to me, but maybe it's right?
var article = await _articleRepo.GetAsync(articleId);
_articleService.RegisterVariant(article, /* variant creation data */);
_variantFactory.Register(article, /* variant creation data */);
I think this should be all, I hope I explained everything well.
I would appreciate any help with this!

C# json deserialiser how to set property

My code is:
namespace RTT_API
{
class routepoint
{
public string description { get; set; }
public string TIPLOC { get { return TIPLOC; } set { SetStationRef(); } }
public string publicTime { get; set; }
public Guid StationRef { get; set; }
public void SetStationRef()
{
SqlCommand comStationRef = new SqlCommand("select uniqueref from station where tiploc=#tiploc", Globals.RTTConn);
comStationRef.Parameters.Add("#tiploc");
comStationRef.Parameters["#tiploc"].Value = TIPLOC;
SqlDataReader rdrStationRef = comStationRef.ExecuteReader();
if (rdrStationRef.HasRows == true)
{
rdrStationRef.Read();
StationRef = rdrStationRef.GetGuid(1);
rdrStationRef.Close();
}
else
{
rdrStationRef.Close();
comStationRef.CommandText="Insert into station(tiploc) values (#tiploc)";
comStationRef.ExecuteNonQuery();
}
rdrStationRef.Close();
}
}
}
I would appreciate help with the following:
If I try to debug the TIPLOC value I receive a Stackoverflow error. It worked OK when the definition was only:
public string TIPLOC { get; set; }
The SetStationRef method doesn't run when the value is set
The TIPLOC value is set by a JSON deserialise command. Is using a custom 'set' method the correct way to set the StationRef value? I tried putting code in the class constructor but it looks like the constructor runs before the JSON deserialise sets the properties? I'm new to C# so trying not to get into any bad practices to start with.
Thanks
This line
public string TIPLOC { get { return TIPLOC; } set { SetStationRef(); } }
will return TIPLOC indefinitely because it is calling itself. Calling return TIPLOC will call the getter of TIPLOC and its just going to keep doing that until it overflows the stack. When you debug the debugger will try and retrieve the value of the property and will get stuck in that loop.
set { SetStationRef(); } also ignores the value. I assume you want to call SetStationRef(value);. In the setter of a property the value the user is trying to set gets passed in as a local variable called value
From the overall look I think what you are trying to achieve is something along the lines of
private string tiploc;
public string TIPLOC
{
get
{
if(tiploc == null)
{
tiplock = GetValueFromDatabase(); // Replace this to retrieve the value
}
tiploc;
}
set
{
SetStationRef(value);
tiploc = value;
}
}
My original three questions were:
If I try to debug the TIPLOC value I receive a Stackoverflow error
The SetStationRef method doesn't run when the value is set -
The TIPLOC value is set by a JSON deserialise command. Is using a custom 'set' method the correct way to set the StationRef value? I tried putting code in the class constructor but it looks like the constructor runs before the JSON deserialise sets the properties? I'm new to C# so trying not to get into any bad practices to start with.
Question 1 was answered by Ben and his help enabled me to find a solution to questions 2 and 3 so a lot of credit is due to him.
I've only added a separate answer because the custom accessor needs to be defined on the StationRef and not the Tiploc (the Tiploc is set by the Deserialisation code) as follows:
public string tiploc { get; set; }
public Guid StationRef
{
get
{
if (stationRef == Guid.Empty)
{
stationRef = Globals.GetStationRef(tiploc);
}
return stationRef;
}
set
{
stationRef = value;
}
}

Differentiating between explicit 'null' and 'not specified' in ASP.NET Core ApiController

This is my very first question after many years of lurking here, so I hope I don't break any rules.
In some of my ASP.NET Core API's POST methods, I'd like to make it possible for clients to provide only the properties they want to update in the body of their POST request.
Here's a simplified version of my code:
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public sealed class FooController : ControllerBase
{
public async Task<IActionResult> UpdateFooAsync(Guid fooGuid, [FromBody]UpdateFooModel model)
{
... Apply updates for specified properties, checking for authorization where needed...
return Ok();
}
}
public sealed class UpdateFooModel
{
[BindProperty] public int? MaxFoo { get; set; }
[BindProperty] public int? MaxBar { get; set; }
}
public sealed class Foo
{
public int? MaxFoo { get; set; }
public int? MaxBar { get; set; }
}
MaxBar and MaxFoo both are nullable integer values, where the null value signifies there's no maximum.
I'm trying to make it possible to let clients send e.g. the following to this endpoint:
Setting MaxBar to null, and setting MaxFoo to 10
{
"maxBar": null,
"maxFoo": 10
}
Setting MaxBar to null, not touching MaxFoo
{ "maxBar": null }
Update MaxBar to 5, not touching MaxFoo
{ "maxBar": 5 }
In my method UpdateFooAsync, I want to update only the properties that have been specified in the request.
However, when model binding occurs, unspecified properties are set to their default values (null for nullable types).
What would be the best way to find out if a value was explicitly set to null (it should be set to null), or was just not present in the request (it should not be updated)?
I've tried checking the ModelState, but it contained no keys for the 'model', only for the Guid typed parameter.
Any other way to solve the core problem would be welcome as well, of course.
Thanks!
Here's a potential solution. Use logic in the setters of the UpdateFooModel class to check for null and assign a different value, such as Int32.MaxValue. The setters are only called when the parameter is passed in. In the example, if null is explicitly passed in, it would be converted to Int32.MaxValue. If the parameter is not specified, the value would remain null. An alternative to using setters would be to use a default constructor and add some logic to set different values based on whether or not parameters were specified. Example:
public sealed class UpdateFooModel
{
private int? _maxFoo;
public int? MaxFoo
{
get
{
return _maxFoo;
}
set
{
_maxFoo = (value == null) ? Int32.MaxValue : value;
}
}
private int? _maxBar;
public int? MaxBar
{
get
{
return _maxBar;
}
set
{
_maxBar = (value == null) ? Int32.MaxValue : value;
}
}
}
Answering my own question here, based on #russ-w 's suggestion (thanks!):
By marking a bool property in each optional property's setter, we can find out if it was provided or not.
public sealed class UpdateFooModel
{
private int? _maxFoo;
private int? _maxBar;
[BindProperty]
public int? MaxFoo
{
get => _maxFoo;
set
{
_maxFoo = value;
MaxFooSet = true;
}
}
public bool MaxFooSet { get; private set; }
[BindProperty]
public int? MaxBar
{
get => _maxBar;
set
{
_maxBar = value;
MaxBarSet = true;
}
}
public bool MaxBarSet { get; private set; }
}
Further improvements or other solutions are still welcome of course!

Update method in web API: How to know which fields to update?

I have a typical web API with a couple of PUT/UPDATE endpoints. These endpoints simply call the underlying service, and do the update.
The service layer, has the typical signature such as Object Update(Object object). What I then do is I basically run the following pseudo code:
var dbobject = _db.Object.Find(object.Id);
dbobject.Field1 = object.Field1;
dbobject.Field2 = object.Field2;
// continue for all fields
_db.SaveChanges();
return GetObjectById(object.Id);
However, this provides a challenge for me.
Lets say we have a consumer of our API. This consumer calls my PUT endpoint (/api/Object/{id}), and the payload is the updated Object.
However, lets say that the object we put don't know about example Field4, then this value would be NULL after the update has been run.
My question is:
What do you do about all those fields the payload does NOT contain?
How do you handle not setting values to NULL you don't expect to be
NULL afterwards?
As one of the possible ways, here can be used mix of NotifyPropertyChanged with automapper
The Idea is to store in DTO object which fields exactly was set, and which stays filled with default value. And use collected data in mapping.
For example DTO object will be
public class Dto
{
private List<string> Changed = new List<string>();
public bool IsChanged(string field) => Changed.Contains(field);
private int _age;
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
// IMPORTANT: field name should fit main object field name
Changed.Add("Name");
}
}
public int Age
{
get { return _age; }
set
{
_age = value;
Changed.Add("Age");
}
}
}
I used Next class for test
public class Human
{
public string Name { get; set; } = "DEFAULT";
public int Age { get; set; } = -1;
}
and automapper configuration will looks like
cfg.CreateMap<Dto, Human>()
.ForAllMembers(s=> s.Condition(d=>d.IsChanged(s.DestinationMember.Name)));
This is a simple example. But it still doesn't prevent to use function IsChanged for some complex/specific logic, use not just a strings but Expressions / MethodInfo, or add custom attributes and use them in automapper configuration (DestinationMember is MethodInfo)
Append
Instead of complex DTO object the information about passed field you can get from Request.Properties in your controller (key ms_querynamevaluepairs value of type Dictionary<string, string>).

Attributes of properties in MetadataType are ignored by EntLib Validation

It's an EntLib-Validator-issue again. I'm playing with EntLib 5.0 in C# and .Net 4.0 on XP pro.
I have some business objects (partial classes) generated by T4 templates. So I decided to put their validation attributes in buddy-classes by using MetadataTypeAttribute as definitely recommended by the documentation of entLib 5.0 (msdn).
But the Validator object I get from the ValidatorFactory doesn't know about the validation attributes, defined in the metadata-class.
The business object is defined like this:
[MetadataType(typeof(PatientMetadata))]
public partial class Patient
{
private string _Name;
private int _DiagnosisCount;
public int DiagnosisCount
{
get
{
return _DiagnosisCount;
}
set
{
if (value != _DiagnosisCount)
{
_DiagnosisCount = value;
}
}
}
public string Name
{
get
{
return _Name;
}
set
{
if (value != _Name)
{
_Name = value;
}
}
}
}
And the metadata class like this, according to documentation:
public class PatientMetadata
{
[RangeValidator(4)]
public int DiagnosisCount { get; set; }
[StringLengthValidator(64, ErrorMessage = "Name must not exceed 64 chars.")]
public string Name { get; set; }
}
If I know try to do validation this way:
var factory = ValidationFactory.DefaultCompositeValidatorFactory;
var validator = factory.CreateValidator<Patient>();
...then watching into validator (during debugging) already says, that it's just an AndCompositeValidator without any children validators.
Again, if I put the validation attributes right in the Patient class, it works perfectly.
By now, I have no real idea, what I'm missing here, since I think doing everything according to the docs.
Thanks in advance to you guys!
The property names of the metadata class must match the property names of the main class.
In your case your metadata class should look like:
public class PatientMetadata
{
[RangeValidator(0, RangeBoundaryType.Inclusive, 10, RangeBoundaryType.Ignore)]
public int DiagnosisCount { get; set; }
[StringLengthValidator(6, ErrorMessage = "Name must not exceed 6 chars.")]
public string Name { get; set; }
}
Also, the docs indicate the accepted approach is to declare all return types as object. However, the docs also talk about using properties but in their example use fields so take it under advisement. :)

Categories

Resources