How to reference Composite ID in Gorm 6 or 7 to prevent IllegalArgumentException: Unable to locate Attribute with the the given name - grails-orm

I have this model which works with Gorm3
class UserDataPoint implements Serializable {
User user
DataPoint dataPoint
static mapping = {
id composite: ['user', 'dataPoint']
}
static List<Long> getReadDataPointIds(User user, List<Long> locationIds = null) {
createCriteria().list {
eq('user', user)
dataPoint {
listing {
location {
inList('id', locationIds)
}
}
}
projections {
property('dataPoint.id')
}
} as List
}
}
but now it runs this exception :
java.lang.IllegalArgumentException: Unable to locate Attribute with the the given
name [dataPoint] on this ManagedType [user.UserDataPoint]
Some debug allows me to understand what is happening, it seems that GORM remove the user and dataPoint properties from HibernateProperties because the are a part of the Composite ID
public abstract class AbstractPersistentEntity<T extends Entity> implements PersistentEntity {
.......
IdentityMapping identifier = mapping != null ? mapping.getIdentifier() : null;
if(identity == null && identifier != null) {
final String[] identifierName = identifier.getIdentifierName();
final MappingContext mappingContext = getMappingContext();
if(identifierName.length > 1) {
compositeIdentity = mappingContext.getMappingSyntaxStrategy().getCompositeIdentity(javaClass, mappingContext);
}
for (String in : identifierName) {
final PersistentProperty p = propertiesByName.get(in);
if(p != null) {
persistentProperties.remove(p);
}
persistentPropertyNames.remove(in);
}
disableDefaultId();
}
......
}
So I understand the problem, but I don't know how to fix it ?
how to reference the composite ID and it fields in the projection ?

It seems to be fixed with this recent version https://github.com/grails/gorm-hibernate5/releases/tag/v7.2.1

Related

Upgrade Solution to use FluentValidation Ver 10 Exception Issue

Please I need your help to solve FluentValidation issue. I have an old desktop application which I wrote a few years ago. I used FluentValidation Ver 4 and Now I'm trying to upgrade this application to use .Net framework 4.8 and FluentValidation Ver 10, but unfortunately, I couldn't continue because of an exception that I still cannot fix.
I have this customer class:
class Customer : MyClassBase
{
string _CustomerName = string.Empty;
public string CustomerName
{
get { return _CustomerName; }
set
{
if (_CustomerName == value)
return;
_CustomerName = value;
}
}
class CustomerValidator : AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(obj => obj.CustomerName).NotEmpty().WithMessage("{PropertyName} is Empty");
}
}
protected override IValidator GetValidator()
{
return new CustomerValidator();
}
}
This is my base class:
class MyClassBase
{
public MyClassBase()
{
_Validator = GetValidator();
Validate();
}
protected IValidator _Validator = null;
protected IEnumerable<ValidationFailure> _ValidationErrors = null;
protected virtual IValidator GetValidator()
{
return null;
}
public IEnumerable<ValidationFailure> ValidationErrors
{
get { return _ValidationErrors; }
set { }
}
public void Validate()
{
if (_Validator != null)
{
var context = new ValidationContext<Object>(_Validator);
var results = _Validator.Validate(context); **// <======= Exception is here in this line**
_ValidationErrors = results.Errors;
}
}
public virtual bool IsValid
{
get
{
if (_ValidationErrors != null && _ValidationErrors.Count() > 0)
return false;
else
return true;
}
}
}
When I run the application test I get the below exception:
System.InvalidOperationException HResult=0x80131509 Message=Cannot
validate instances of type 'CustomerValidator'. This validator can
only validate instances of type 'Customer'. Source=FluentValidation
StackTrace: at
FluentValidation.ValidationContext1.GetFromNonGenericContext(IValidationContext context) in C:\Projects\FluentValidation\src\FluentValidation\IValidationContext.cs:line 211 at FluentValidation.AbstractValidator1.FluentValidation.IValidator.Validate(IValidationContext
context)
Please, what is the issue here and How can I fix it?
Thank you
Your overall implementation isn't what I'd consider normal usage however the problem is that you're asking FV to validate the validator instance, rather than the customer instance:
var context = new ValidationContext<Object>(_Validator);
var results = _Validator.Validate(context);
It should start working if you change it to:
var context = new ValidationContext<object>(this);
var results = _Validator.Validate(context);
You're stuck with using the object argument for the validation context unless you introduce a generic argument to the base class, or create it using reflection.

Default values in list query parameter

I am trying to display default values in Swashbuckle. Is there a way to define default values in a query list parameter in a .NET Core API.
Something like:
[HttpGet("test")]
public ActionResult<string> TestFunc([FromQuery, BindRequired] List<string> testList = ["value1", "value2"]) {
//Do some stuff
return Ok(results);
}
As far as I know, we couldn't set the default parameter value must be compile-time constant which means we couldn't set a default value for a list or array of string.
That means there is no way to set the defualt value inside the web api paramter.
If you want to show the default value inside the swagger. You could create a class which inherit from IOperationFilter.
Then you could check the paramter name, if the name is equals the testList ,you could set the custom description.
More details, you could refer to below codes example:
Custom class:
public class ParameterClass : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (operation.Parameters == null)
{
return;
}
foreach (var parameter in operation.Parameters) {
if (parameter.Name == "testList")
{
parameter.Description = #"Default value: ['value1', 'value2']";
}
}
}
}
Register the swaggergen with filter :
services.AddSwaggerGen(c =>
{
c.OperationFilter<ParameterClass>();
});
Result:
Update:
If you want to set the parameter like query string, you could modify the apply method as below:
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (operation.Parameters == null)
{
return;
}
foreach (var parameter in operation.Parameters) {
if (parameter.Name == "testList")
{
parameter.In = 0;
parameter.Description = #"['value1', 'value2']";
parameter.Schema = new OpenApiSchema() { Type = "string" ,Items = null };
}
}
}
Result:

Serializing properties

I have searched everywhere in CF docs, FAQ's, Blog and SO entries but still can't understand why this model:
<cf:project defaultNamespace="Humanisme" xmlns:cf="http://www.softfluent.com/codefluent/2005/1" xmlns:cfx="http://www.softfluent.com/codefluent/modeler/2008/1" xmlns:cfom="http://www.softfluent.com/codefluent/producers.model/2005/1" xmlns:cfps="http://www.softfluent.com/codefluent/producers.sqlserver/2005/1" xmlns:cfsm="http://www.softfluent.com/codefluent/producers.servicemodel/2007/1" defaultKeyPropertyTypeName="int" defaultMaxLength="240" persistencePropertyNameFormat="{1}" createDefaultMethodForms="true" createDefaultApplication="false" createDefaultHints="false">
<cf:import path="Default.Surface.cfp" />
<cf:producer name="Business Object Model (BOM)" typeName="CodeFluent.Producers.CodeDom.CodeDomProducer, CodeFluent.Producers.CodeDom">
<cf:configuration compileWithVisualStudio="true" compile="false" codeDomProviderTypeName="CSharp" targetDirectory="..\Humanisme.classes" produceWebMembershipProvider="false" produceWebProfileProvider="false" produceWebBasicAuthenticationModule="false" cfx:targetProject="..\Humanisme.classes\Humanisme.classes.csproj" cfx:targetProjectLayout="Update">
<subProducer typeName="CodeFluent.Producers.ServiceModel.ServiceProducer, CodeFluent.Producers.ServiceModel, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1bb6d7cccf1045ec" compileWithVisualStudio="true" compile="false" codeDomProviderTypeName="CSharp" targetDirectory="..\Humanisme.webApi" silverlightTargetVersion="Unspecified" dataAnnotationsProductionModes="NoAnnotations, All" jsonOptions="EnableJson" outputName="HumanismeService" cfx:targetProject="..\Humanisme.webApi\Humanisme.webApi.csproj" cfx:targetProjectLayout="Update" produceProxy="False" />
</cf:configuration>
</cf:producer>
(....)
<cf:entity name="EtapaVital" namespace="Humanisme" categoryPath="/Humanisme">
<cf:property name="Id" key="true" />
<cf:property name="Nom" />
<cf:property name="Idioma" typeName="{0}.EtapaVitalIdiomaCollection" relationPropertyName="Etapa" />
<cf:property name="Documents" typeName="{0}.DocumentCollection" relationPropertyName="EtapaVital" />
</cf:entity>
Renders to these serializing attributes:
RowVersion and EntityState get the serializing attributes.
EntityDisplayName does not.
[System.Runtime.Serialization.DataMemberAttribute(Order=2147483647)]
private CodeFluent.Runtime.CodeFluentEntityState _entityState;
public EtapaVital()
[System.Runtime.Serialization.DataMemberAttribute()]
public virtual string EntityKey
{
get
{
return this.Id.ToString();
}
set
{
this.Id = ((int)(ConvertUtilities.ChangeType(value, typeof(int), -1)));
}
}
public virtual string EntityDisplayName
{
get
{
return this.Nom;
}
}
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
[System.ComponentModel.DataObjectFieldAttribute(false, true)]
[System.ComponentModel.TypeConverterAttribute(typeof(CodeFluent.Runtime.Design.ByteArrayConverter))]
[System.Runtime.Serialization.DataMemberAttribute()]
public byte[] RowVersion
{
get
{
return this._rowVersion;
}
set
{
if (((value != null)
&& (value.Length == 0)))
{
value = null;
}
this._rowVersion = value;
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("RowVersion"));
}
}
[System.ComponentModel.DefaultValueAttribute(((int)(-1)))]
[System.Xml.Serialization.XmlElementAttribute(IsNullable=false, Type=typeof(int))]
[System.ComponentModel.DataObjectFieldAttribute(true)]
[System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false, Order=100)]
public int Id
{
get
{
return this._id;
}
set
{
if ((System.Collections.Generic.EqualityComparer<int>.Default.Equals(value, this._id) == true))
{
return;
}
int oldKey = this._id;
this._id = value;
try
{
this.OnCollectionKeyChanged(oldKey);
}
catch (System.ArgumentException )
{
this._id = oldKey;
return;
}
this.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Modified;
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("Id"));
}
}
[System.ComponentModel.DefaultValueAttribute(default(string))]
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Type=typeof(string))]
[System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false, Order=200)]
public string Nom
{
get
{
return this._nom;
}
set
{
this._nom = value;
this.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Modified;
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("Nom"));
}
}
[System.Xml.Serialization.XmlIgnoreAttribute()]
public Humanisme.EtapaVitalIdiomaCollection Idioma
{
get
{
if ((this._idioma == null))
{
if (((this.Id == -1)
|| (this.EntityState.Equals(CodeFluent.Runtime.CodeFluentEntityState.Created) == true)))
{
this._idioma = new Humanisme.EtapaVitalIdiomaCollection(this);
return this._idioma;
}
this._idioma = Humanisme.EtapaVitalIdiomaCollection.LoadByEtapa(this);
}
return this._idioma;
}
}
[System.Xml.Serialization.XmlIgnoreAttribute()]
public Humanisme.DocumentCollection Documents
{
get
{
if ((this._documents == null))
{
if (((this.Id == -1)
|| (this.EntityState.Equals(CodeFluent.Runtime.CodeFluentEntityState.Created) == true)))
{
this._documents = new Humanisme.DocumentCollection(null, this);
return this._documents;
}
this._documents = Humanisme.DocumentCollection.LoadByEtapaVital(this);
}
return this._documents;
}
}
public virtual CodeFluent.Runtime.CodeFluentEntityState EntityState
{
get
{
return this._entityState;
}
set
{
if ((System.Collections.Generic.EqualityComparer<CodeFluent.Runtime.CodeFluentEntityState>.Default.Equals(value, this.EntityState) == true))
{
return;
}
if (((this._entityState == CodeFluent.Runtime.CodeFluentEntityState.ToBeDeleted)
&& (value == CodeFluent.Runtime.CodeFluentEntityState.Modified)))
{
return;
}
if (((this._entityState == CodeFluent.Runtime.CodeFluentEntityState.Created)
&& (value == CodeFluent.Runtime.CodeFluentEntityState.Modified)))
{
return;
}
this._entityState = value;
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("EntityState"));
}
}
It seems there are not related property options on the model surface "Properties" window and I'm not able to find the xml tags or attributes in the cfp file.
I will be very grateful if you can give me any clue or reference where to start learning the CodeFluent Model options that lead to these outputs in the classes.
Thanks again, I'm really turning into a CodeFluent enthusiast!
The RowVersion property is generated by CodeFluent Entities because you set the concurrency mode to Optimistic (documentation). If you don't have the latest value of this property, you cannot save the entity (CodeFluentConcurrencyException). So this property needs to be serialized.
The EntityDisplayName is for UI purposes. Its value is computed from one or many properties. So there is no need to serialize it as you can compute it client side.
There is currently no attributes to change the serialization behavior for these specific properties. However you can write a custom sub producer to add/remove serialization attributes on properties. You'll find an example that adds Json.NET specific attributes on properties (GitHub)
Here's are some attributes to change the serialization behavior:
serializable (Entity): Determines if this entity is marked as serializable
serializeEntityKey (Entity): Determines if the EntityKey must be serialized
serializeTypeName (Entity): Determines if the TypeName must be serialized
serializationMode (Property): documentation
dataMember or includeInSerialization (Property) => Prefer using serializationMode
serializationOrder (Property): Defines the sort order of this property when serialized
serializationNullable (Property): Determines if the property can be skipped when serialized
serializePrivateMember (Property): Determines if the private field of a non-serializable property is serializable
publicSerializationCallbacks (Project): Determines if the serialization callback methods are public (OnEntityDeserializing, OnEntityDeserialized, etc.)

NHibernate Dynamic Component Default Value Issue

All of my entities (that are mapped to a database table) inherit from an entity class with a dynamic component on it called Attributes e.g.:
public abstract class Entity<T> {
public virtual T Id { get; set; }
private IDictionary _attributes;
public virtual IDictionary Attributes {
get { return _attributes ?? (_attributes = new Hashtable()); }
set { _attributes = value; }
}
}
The Attributes collection allows me to add extra fields to each entity without directly changing the entity itself. This allows me to make my application more modular.
For example say I have the following entity:
public class User : Entity<int> {
public virtual string Name { get; set; }
}
Now say I have a Forum module which needs a NumPosts property against the User. I would add the field against the Users table in the database. This field is non nullable and has a default value of 0. I then map the field using the dynamic component against the User entity.
However when I try inserting the user by saying:
session.Save(new User() { Name = "Test" });
It throws an error as it's expecting me to set a value for NumPosts and the generated SQL would be something like:
INSERT INTO Users (Name, NumPosts) VALUES ('Test', NULL)
However NumPosts does not allow nulls and hence the error. Ideally I'd like it to say the following if the Attributes collection does not contain an entry for NumPosts:
INSERT INTO Users (Name) VALUES ('Test')
An alternative is to say the following which would work fine:
session.Save(new User() { Name = "Test", Attributes = new Hashtable() { { "NumPosts", 0 } } });
The problem I have is that I don't want the modules to have a dependency on each other and I can't really say this.
For reference here's a bare bones version of session factory method which maps the NumPosts field:
return Fluently.Configure()
...
.ExposeConfiguration(c => {
// Get the persistent class
var persistentClass = c.GetClassMapping("User");
// Create the attributes component
var component = new Component(persistentClass);
// Create a simple value
var simpleValue = new SimpleValue(persistentClass.Table);
// Set the type name
simpleValue.TypeName = "Int32";
// Create a new db column specification
var column = new Column("NumPosts");
column.Value = simpleValue;
column.Length = 10;
column.IsNullable = false;
column.DefaultValue = "0";
// Add the column to the value
simpleValue.AddColumn(column);
// Ad the value to the component
component.AddProperty(new Property() { Name = column.Name, Value = simpleValue });
// Add the component property
persistentClass.AddProperty(new Property() { Name = "Attributes", Value = component });
})
.BuildConfiguration();
I'd appreciate if someone could let me know if this is possible. Thanks
You know how to make it working as described above:
... An alternative is to say the following which would work fine:
session.Save(new User()
{
Name = "Test", Attributes = new Hashtable() { { "NumPosts", 0 } }
});
... The problem I have is that I don't want the modules to have a dependency on each other and I can't really say this...
In case, that the biggest issue is the explicit Attributes initialization ("...I don't want the modules to have a dependency...") we can use:
12.2. Event system
So, with Listener like this:
[Serializable]
public class MyPersistListener : NHibernate.Event.ISaveOrUpdateEventListener
{
public void OnSaveOrUpdate(SaveOrUpdateEvent #event)
{
var entity = #event.Entity as Entity<int>; // some interface IHaveAttributes
if (entity == null) // would be more appropriate
{
return;
}
var numPosts = entity.Attributes["NumPosts"] as int?;
if (numPosts.HasValue)
{
return;
}
entity.Attributes["NumPosts"] = 0;
}
}
Based on this doc snippet:
Configuration cfg = new Configuration();
ILoadEventListener[] stack = new ILoadEventListener[] { new MyLoadListener(), new DefaultLoadEventListener() };
cfg.EventListeners.LoadEventListeners = stack;
This should be the init in our case:
.ExposeConfiguration(c => {
var stack = new ISaveOrUpdateEventListener [] { new MyPersistListener() };
c.EventListeners.SaveEventListeners= stack;

Insert in nested field

I'm a new user in LINQ to SQL and I have some problems using it.
I've used LINQ to SQL Designer and I have created my classes, mapped on the DB tables.
In particular, I have one class, named voice:
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.voce")]
public partial class voce : INotifyPropertyChanging, INotifyPropertyChanged
{
private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
private int _id_voce;
... other private fields;
private int _category;
private EntityRef<category> _category1;
public voce()
{
this._riepilogo = new EntitySet<riepilogo>(new Action<riepilogo>(this.attach_riepilogo), new Action<riepilogo>(this.detach_riepilogo));
this._hera = default(EntityRef<hera>);
this._category1 = default(EntityRef<category>);
OnCreated();
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_id_voce", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
public int id_voce
{
get
{
return this._id_voce;
}
set
{
if ((this._id_voce != value))
{
this.Onid_voceChanging(value);
this.SendPropertyChanging();
this._id_voce = value;
this.SendPropertyChanged("id_voce");
this.Onid_voceChanged();
}
}
}
......
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_category", DbType="Int NOT NULL")]
public int category
{
get
{
return this._category;
}
set
{
if ((this._category != value))
{
if (this._category1.HasLoadedOrAssignedValue)
{
throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
}
this.OncategoryChanging(value);
this.SendPropertyChanging();
this._category = value;
this.SendPropertyChanged("category");
this.OncategoryChanged();
}
}
}
As you can see, voce class has a field named category that refers to a table named category.
When I add a new voce to my database, I create a new voce istance and, using the DataContext, i simply add it, using:
voce v = new voce(){...field, category1 = //create or retrieve category};
In particular, the category field is retrieved from the DB if already exists or, if not, it is inserted, before I insert the voice.
The problem is that when I add the voice in the database:
datacontext.InsertOnSubmit(v);
datacontext.SubmitChanges();
it inserts the category again, failing with the unique contraint.
How can I add a voice without adding every nested object?
Thank you and sorry for my bad English.
internal category GetCategoryFromDescription (string desc, Utility.VOICE_MODALITY mode)
{
bool type = mode == Utility.VOICE_MODALITY.ENTRATA ? true : false;
var query = from cat in dc.category
where cat.description == desc && cat.type == type
select cat;
if (query.Count() == 0)
{
category newC = new category() { description = desc };
dc.category.InsertOnSubmit(newC);
dc.SubmitChanges();
return newC;
}
else
return query.Single();
}