What is the "Func<object> modelAccessor" parameter for in MVC's DataAnnotationsModelMetadataProvider? - asp.net-mvc-2

It's one of the parameters supplied to the CreateMetadata method (which you override if extending metadata support).
ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes,
Type containerType,
Func<object> modelAccessor, <<--THIS ONE
Type modelType,
string propertyName)
I had assumed that it allowed you to access the model object itself (e.g. for setting metadata based on model values), however when I try to use it to cast to my model object I just get null.
Entity ent = (Entity)modelAccessor(); // = Null
If I've missunderstood, can anyone explain what it's purpose is? Or alternatively, how to properly use it?
Thanks

We originally had that as "object model", rather than "Func modelAccessor". We had to change it late in MVC 2's ship cycle.
The purpose is to delay retrieving the actual value of the model until such point as you know you're going to need it (that is, until you call ModelMetadata.Model).
The problem it solves is actually a rather esoteric one related to model binding against a LINQ to SQL class that has a foreign key reference in it. The problem is, if you've retrieved the child object which is represented by a foreign key relationship (which usually means a delay load of that object), then you're no longer allowed to choose a new child object by setting the foreign key ID property. It's very common to model bind the foreign key ID (and not the whole foreign key entity) when model binding, but if we'd retrieved the foreign key entity object (for the purposes of populating the ModelMetadata class) then that binding would no longer be legal, and actually throw an exception. Since ModelMetadata is used for both directions of models -- inbound, via model binding, and outbound, via HTML generation -- we needed to introduce the layer of indirection to protect your ability to use it in both scenarios without disrupting LINQ to SQL's rules.

The modelAccessor parameter does not point to an instance of the object, but rather it is a function that will access some attribute of your object. The Func "encapsulates a method that has no parameters and returns a value of the type specified by the TResult parameter." For example, if we have following class:
public class Bar(){
[DisplayName("I am Foo.")]
public string Foo{get;}
}
When the CreateMetaData is called, it will be to create meta data for the Foo property and the modelAccessor will be a function that returns the value of Foo.
I did a little digging and found a way to get to the instance of the object, but it requires using reflection. You can do the following to get the Bar class in my example:
if (modelAccessor != null)
{
//Use reflection to get the private field that holds the Bar object.
FieldInfo container = modelAccessor.Target.GetType().GetField("container");
//Invoke field on the modelAccessor target to get the instance of the Bar object.
Bar myObject = (Bar)container.GetValue(modelAccessor.Target);
}
I've only run this against a simple test case, so your mileage may vary, but hopefully this will help clarify what is going on.

Related

Assign non persisted object to view using extbase

While trying to assign an object to a view in my controller action I get the following message because this object is not persisted:
Could not serialize Domain Object Vendor\Extension\Domain\Model\Object. It is neither an Entity with identity properties set, nor a Value Object.
Is there any possibility to add this object to the view without creating a databaseentry?
The exception [InvalidArgumentValueException('Could not serialize Domain Object $className. It is neither an Entity with identity properties set, nor a Value Object.', 1260881688)][1] is thrown in the UriBuilder, thus when a model shall be used as argument for creating a link.
The instance of Vendor\Extension\Domain\Model\Object must either fulfill these requirements:
can be represented as array (is array or implements Iterator interface) OR
extends TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject AND one of
extends TYPO3\CMS\Extbase\DomainObject\AbstractValueObject OR
having a valid uid, not null
Thus, if you instantiated the object directly in the controller, the uid property is not defined yet. This property is assigned if domain objects are fetched or added with a repository.
TypeConverters
TypeConverters allow to convert from a given identifier (some string representation, hash-value, ...) to a proper domain object. The following links show how to do that for the concept of an IBAN (International Bank Account Identifier).
IBAN model
TypeConverter to create object from string
TypeConverters have to registerd in ext_localconf.php like this:
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerTypeConverter(
\H4ck3r31\BankAccountExample\Domain\Property\TypeConverter\IbanTypeConverter::class
);
The Iban object can be used then in your controller:
public function someAction(Iban $iban) { ... }
Use array representation of your object
Another alternative could be to assign an array representation of the domain object to the view and use that to fill the link arguments:
$this->view->assign('myObject', $object->toArray());
When invoking a controller action, the object is reconstituted from the submitted array keys and are used as properties - thus array keys and properties must have the same naming, or a persistence column mapping is defined.
public function someAction(MyObject $object) { ... }
In my previously mentioned bank account example it looks like this:
BankDto model
Controller action invocation
The term "Dto" is the abbreviation for "Data Transfer Object", thus it's not a real domain entity, does not have a proper UID and is just used to encapsulate information in a domain object when passing that to different components.

How to completely customize the way that type information gets written to a document by Spring Data MongoDB?

Is it possible to use different type attribute (instead of _class) for each polymorphic collection like it's implemented in Doctrine(PHP) or Jackson libraries? Current solution allows to store type information in document field. By default it is a full class name stored in the field named _class.
We can easy change it to save some custom string (alias) instead of full class name and change default discriminator field name from _class to something else.
In my situation I'm working on legacy database while legacy application is still in use. Legacy application uses Doctrine (PHP) ODM as datalayer.
Doctrine allows to define discriminator field name (_class in SpringData) by annotation and have it different for each collection.
In Spring Data when I pass typeKey to DefaultMongoTypeMapper it used for all collections.
Thanks.
// MyCustomMongoTypeMapper.java
// ...
#SuppressWarnings("unchecked")
#Override
public <T> TypeInformation<? extends T> readType(DBObject source, TypeInformation<T> basicType) {
Assert.notNull(basicType);
Class<?> documentsTargetType = null;
Class<? super T> parent = basicType.getType();
while (parent != null && parent != java.lang.Object.class) {
final String discriminatorKey = getDiscriminatorKey(parent); //fetch key from annotation
if (null == discriminatorKey) {
parent = parent.getSuperclass();
} else {
accessor.setKey(discriminatorKey);
return super.readType(source, basicType);
}
}
accessor.resetKey();
return super.readType(source, basicType);
}
Something that should work for you is completely exchanging the MongoTypeMapper instance that MappingMongoConverter uses. As you discovered the already available implementation assumes a common field name and takes yet another strategy to either write the fully-qualified class name or an alias or the like.
However, you should be able to just write your own and particularly focus on the following methods:
void writeType(TypeInformation<?> type, DBObject dbObject) — you basically get the type and have complete control over where and how to put that into the DBObject.
<T> TypeInformation<? extends T> readType(DBObject source, TypeInformation<T> defaultType); — you get the type declared on the reading side (i.e. usually the most common type of the hierarchy) and based on that have to lookup the type from the given source document. I guess that's exactly the inverse of what's to be implemented in the other method.
On a final note, I would strongly recommend against using different type field names for different collections as on the reading side you might run into places where just Object is declared on the property and you basically don't get no clue where to even look for in the document.

MongoDB C# Select specific columns

I know that MongoDb C# driver doesn't support projections so I searched a little bit and I found that many people uses a mongoCursor to perform such queries, I'm trying to select only specific fields and my code is the following:
public T GetSingle<T>(Expression<Func<T, bool>> criteria,params Expression<Func<T, object>>[] fields) where T : class
{
Collection = GetCollection<T>();
return Collection.FindAs<T>(Query<T>.Where(criteria)).SetFields(Fields<T>.Include(fields)).SetLimit(1).SingleOrDefault();
}
I got and custom repository for users on top of that:
public User GetByEmail(string mail, params Expression<Func<User, object>>[] fields)
{
return GetSingle<User>(x=>x.Email==mail,fields);
}
this is the usage:
_repository.GetByEmail(email, x=>x.Id,x=>x.DisplayName,x=>x.ProfilePicture)
but I'm getting the fields included in the parameter but also all the Enums,dates and Boolean values that are part of the class User, the values that are string and not included in the field list are null so that's fine
what can I do to avoid that?
By using SetFields, you can specify what goes through the wire. However, you're still asking the driver to return hydrated objects of type T, User in this case.
Now, similar to say an int, enum and boolean are value types, so their value can't be null. So this is strictly a C#-problem: there is simply no value for these properties to indicate that they don't exist. Instead, they assume a default value (e.g. false for bool and 0 for numeric types). A string, on the other hand, is a reference type so it can be null.
Strategies
Make the properties nullable You can use nullable fields in your models, e.g.:
class User {
public bool? GetMailNotifications { get; set; }
}
That way, the value type can have one of its valid values or be null. This can, however, be clumsy to work with because you'll have to do null checks and use myUser.GetMailNotifications.Value or the myUser.GetMailNotifications.GetValueOrDefault helper whenever you want to access the property.
Simply include the fields instead this doesn't answer the question of how to it, but there are at least three good reasons why it's a good idea to include them:
When passing a User object around, it's desirable that the object is in a valid state. Otherwise, you might pass a partially hydrated object to a method which passes it further and at some point, someone attempts an operation that doesn't make sense because the object is incomplete
It's easier to use
The performance benefit is negligible, unless you're embedding huge arrays which I would suggest to refrain from anyway and which isn't the case here.
So the question is: why do you want to make all the effort of excluding certain fields?

validating that a field is unique using Bean Validation (or JSF)

I have an simple object that has a name
public class Foo {
private String name
}
Each user on the site may have up to 10 Foo's associated with them. Within this context, when a new Foo is created, I would like to validate that there isn't another foo associated with the same user that already exists.
I could Create a custom Bean Validator But annotations require the paramaeters to be defined during compilation. How would I then pass across the names of the existing Foos?
As suggested in various places, I could use EL expressions as an alternative way to pick up the data. This feels like using a sledgehammer to crack a nut. It also brings in a whole bunch of potential issues to consider least of all being ease of testing.
I could do class-wide validation using a boolean field
#AssertTrue(message="Name already exists")
public boolean isNameUnique() {
return (existingNames.contains(name));
}
But the validation message would not show up next to the name field. It is a cosmetic issue and this can be a backup plan. However, its not ideal.
Which brings me to the question:
Is there a simple way to write a Bean Validator that can check the value against a collection of values at the field level and meet the following restrictions ?
Previous values determined at runtime
Not using things like EL expressions
Field level validation instead of class level.
EDIT in reponse to Hardy:
The Foo class is an entity persisted within a database. They are picked up and used through a DAO interface.
I could loop through the entities but that means plugging the DAO into the validator and not to mention that the I would need to write the same thing again if I have another class that too has this constraint.
It would help to see how you want to use the Foo class. Can you extend your example code? Are they kept in a list of Foo instances. A custom constraint seems to be a good fit. Why do you need to pass any parameters to the constraints. I would just iterate over the foos and check whether the names are unique.

Entity Framework Validation & usage

I'm aware there is an AssociationChanged event, however, this event fires after the association is made. There is no AssociationChanging event. So, if I want to throw an exception for some validation reason, how do I do this and get back to my original value?
Also, I would like to default values for my entity based on information from other entities but do this only when I know the entitiy is instanced for insertion into the database. How do I tell the difference between that and the object getting instanced because it is about to be populated based on existing data? Am I supposed to know? Is that considiered business logic that should be outside of my entity business logic?
If that's the case, then should I be designing controller classes to wrap all these entities? My concern is that if I deliver back an entity, I want the client to get access to the properties, but I want to retain tight control over validations on how they are set, defaulted, etc. Every example I've seen references context, which is outside of my enity partial class validation, right?
BTW, I looked at the EFPocoAdapter and for the life of me cannot determine how to populate lists of from within my POCO class... anyone know how I get to the context from a EFPoco Class?
This is in reply to a comment I left. Hopefully this answers your question, Shimmy. Just comment, and I will shorten it or remove it if it doesn't answer your question.
You will need both INotifyPropertyChanging and INotifyPropertyChanged interfaces to be implemented on your class (unless it is something like an entity framework object, which I believe implements these internally).
And before you set a value to this property, you will need to raise NotifyPropertyChanging.PropertyChanging event, using the name of the property in PropertyChangingEventArgs constructor.
And after you set this value you need to raise NofityPropertyChanged.PropertyChanged event, again using the name of the property this is being raised in PropertyChangedEventArgs constructor.
Then you have to handle the PropertyChanging and PropertyChanged events. In the PropertyChanging event, you need to cache the value. In the PropertyChanged event, you can compare and throw an exception.
To get the property from PropertyChanging/PropertyChanged event args, you need to use relfection.
// PropertyName is the key, and the PropertyValue is the value.
Dictionary <string, object> propertyDict = new Dictionary<object, object>();
// Convert this function prototype to C# from VBNet. I like how Handles is descriptive.
Public Sub PropertyChanging(sender As object, e As PropertyChangingEventArgs) Handles Foo.PropertyChanging
{
if (sender == null || preventRecursion)
{
return;
} // End if
Type senderType = sender.GetType();
PropertyInfo info = senderType.GetProperty(e.PropertyName);
object propertyValue = info.GetValue(sender, null);
// Change this so it checks if e.PropertyName already exists.
propertyDict.Add(e.PropertyName, propertyValue);
} // End PropertyChanging() Event
// Convert this function prototype to C# from VBNet. I like how Handles is descriptive.
Public Sub PropertyChanged(sender As object, e As PropertyChangedEventArgs) Handles Foo.PropertyChanged
{
if (sender == null || preventRecursion)
{
return;
} // End if
Type senderType = sender.GetType();
PropertyInfo info = senderType.GetProperty(e.PropertyName);
object propertyValue = info.GetValue(sender, null);
// Change this so it makes sure e.PropertyName exists.
object oldValue = propertyDict(e.PropertyName);
object newValue = propertyValue;
// No longer needed.
propertyDict.Remove(e.PropertyName);
if (/* some condition */)
{
try {
preventRecursion = true;
info.SetValue(oldValue, null);
Throw New Exception();
} finally {
preventRecursion = false;
} // End try
} // End if
} // End PropertyChanging() Event
Notice how I am using PreventRecursion, which is a boolean I forgot to add above these methods? When you reset the property back to its previous value, these events will be recalled.
tl;dr
Now you could derive a single event which inherits from INotifyPropertyChanged, but uses an argument which holds an Object representing the previous value as well as the Property Name. And that would reduce the number of events being fired down to one, have similar functionality, and have backwards compatibility with INotifyPropertyChanged.
But if you want to handle anything before the property gets set (say the property does an irreversible change or you need to setup other properties before setting that variable, otherwise an exception will be thrown) you won't be able to do that.
Overall, this method is a very old way of doing things. I would take Poker Villian's answer and have invalid data able to be entered. But disallow saving to a database.
Entity Framework has some excellent code towards validation. You add validation to your properties via attributes. And then it takes care of the work of processing those attributes. Then you can make a property called IsValid, which calls Entity Framework specific validation. It also distinguishes both field errors (like typing in the wrong characters or having a string too long), and class errors (like having missing data or conflicting keys).
Then you can bind IsValid to controls validation, and they will display a red bubble while invalid data is entered. Or you could just implement IsValid validation yourself. But If IsValid is false, SaveChanges event would need to cancel saving.
btw. The code provided will not compile and is pseudocode only (mixing vb and c#). But I believe it is much more descriptive than c# alone--showing exactly what is being handled.
Concerning your first question, I would simply implement the changes to the associations as business logic. For example, if you add a Teacher class with multiple Student, do not add students like
aTeacher.Students.Add(new Student)
instead, create a AddStudent method
public Student AddNewStudent(string name, string studentID)
{
Student s = new Student( name, studentID);
s.Teacher = this; // changes the association
return s;
}
That way you have full control on when associations are changed. Of course that what prevents another programmer from adding a student directly? On the Student side, you can set the Teacher setter to private (and change the constructor to accept a teacher or similar). On the teacher side, how to make the Students collection non-insertable? I'm not certain... maybe transforming it in a custom collection that doesn't accept inserts.
Concerning the second part of your question, you could probably use the OnVarNameChanging events. If the EntityState is 'New' then you can apply your logic that fetches the real values.
There is also an event that fires when you save changes (OnSavingChanges?) that you could use to determine which objects are new and set some values.
But maybe the simplest solution is to always set the defaults in the constructor and they will get overwritten if the data is loaded from the DB.
Good luck
Create a factory that produces instances for you depending on your need like:
getStudent(String studentName, long studentId, Teacher teacher) {
return new Student(studentName, studentId);
}
getStudentForDBInseration(String studentName, long studentId, Teacher teacher) {
Student student = getStudent(studentName, studentId);
student = teacher;
//some entity frameworks need the student to be in the teachers student list
//so you might need to add the student to the teachers student list
teacher.addStudent(student);
}
It's a serious lack not having an AssociationChanging (that inherits from CancelEventArgs) event.
It bothers me also very much, therefore I reported this to Microsoft Connect Please vote here!
And BTW, I also think this is also stupid that the PropertyChangingEventArgs doesn't inherit CancelEventArgs, since cancelling with an exception is not always the elegant solution, besides, throwing exceptions cost more performance than calling the OnPropertyChangingEvent then check for the returned e.Cancel, so does it cost less than raising the PropertyChangingEvent, which you anyway call them both.
Also an exception can be thrown at the handler anyway instead of marking e.Cancel as true, for those who insist to go the Exception way. Vote Here.
To maybe answer part of your question or expound on ADB's answer you can user ObjectStateManager.GetObjectStateEntry to find the state of the entities and write your custom default logic.
SaveChanges is the method on the context that you can use, or SavingChanges is the event that occurs before SaveChanges is called.
You can override SaveChanges and only call base.SaveChanges if you don't want to abort the change
There is also a ObjectMaterialized event for the context.
Between the two you can stick all your validation and creation code in one location, which may be appropriate if they are complex and include values of other objects etc..