I have a model with some inheritance worked into it, but here's the thing, I want to apply different HTML attributes depending on which type it is. So my thought was to create a property, I'll call it DisplayString, which is used as the first argument to String.Format. DisplayString is used to allow me to do something like this:
Classes:
public class Reaction
{
public string ReactionString { get; set; }
public string DisplayString { get; set; }
public Reaction(string reactionString)
{
ReactionString = reactionString;
DisplayString = "It was {0}!"
}
public Reaction(string reactionString, string displayString)
{
ReactionString = reactionString;
DisplayString = displayString;
}
}
public class GoodReaction : Reaction
{
public GoodReaction()
{
base("awesome");
}
}
public class BadReaction : Reaction
{
public GoodReaction()
{
base("horrible");
}
}
public class AverageReaction : Reaction
{
public GoodReaction()
{
base("alright", "It kinda was {0}...");
}
}
View:
#model Reaction
#String.Format(Model.DisplayString, "<strong>" + Model.ReactionString + "</strong>");
So basically every subclass has a string I want to display, but I have varying text AND varying markup. Of course doing it this way just results with the strong tags rendered as text. So what are my options? Feel free to ask any questions needed to clarify, hopefully I got my question across properly. Thanks!
EDIT:
So I wrapped it in Html.Raw() and that did the trick, although I'm sure there is something bad about doing that and I should probably encode each piece, then add tags, then raw, or something, but for my purposes it works.
I would create a display template for each concrete class, and MVC will be smart enough to pick the right one.
E.g when you call #Html.DisplayFor(model => model.Reaction), where Reaction is declared as the base class, MVC will work out what type it is, then look for a template matching that type, if it doesn't find it, it will look for a template of the base type, then finally fallback to the default template. I do this a lot in my application.
Sounds like you should also make the Reaction class abstract, and the ctor's protected.
Related
We have a lot of Dto classes in our project and on various occasions SELECT them using Expressions from the entity framework context. This has the benefit, that EF can parse our request, and build a nice SQL statement out of it.
Unfortunatly, this has led to very big Expressions, because we have no way of combining them.
So if you have a class DtoA with 3 properties, and one of them is of class DtoB with 5 properties, and again one of those is of class DtoC with 10 properties, you would have to write one big selector.
public static Expression<Func<ClassA, DtoA>> ToDto =
from => new DtoA
{
Id = from.Id,
Name = from.Name,
Size = from.Size,
MyB = new DtoB
{
Id = from.MyB.Id,
...
MyCList = from.MyCList.Select(myC => new DtoC
{
Id = myC.Id,
...
}
}
};
Also, they cannot be reused. When you have DtoD, which also has a propertiy of class DtoB, you would have to paste in the desired code of DtoB and DtoC again.
public static Expression<Func<ClassD, DtoD>> ToDto =
from => new DtoD
{
Id = from.Id,
Length = from.Length,
MyB = new DtoB
{
Id = from.MyB.Id,
...
MyCList = from.MyCList.Select(myC => new DtoC
{
Id = myC.Id,
...
}
}
};
So this will escalate pretty fast. Please note that the mentioned code is just an example, but you get the idea.
I would like to define an expression for each class and then combine them as required, as well as EF still be able to parse it and generate the SQL statement so to not lose the performance improvement.
How can i achieve this?
Have you thought about using Automapper ? You can define your Dtos and create a mapping between the original entity and the Dto and/or vice versa, and using the projection, you don't need any select statements as Automapper will do it for you automatically and it will project only the dto's properties into SQL query.
for example, if you have a Person table with the following structure:
public class Person
{
public int Id { get; set; }
public string Title { get; set; }
public string FamilyName { get; set; }
public string GivenName { get; set; }
public string Initial { get; set; }
public string PreferredName { get; set; }
public string FormerTitle { get; set; }
public string FormerFamilyName { get; set; }
public string FormerGivenName { get; set; }
}
and your dto was like this :
public class PersonDto
{
public int Id { get; set; }
public string Title { get; set; }
public string FamilyName { get; set; }
public string GivenName { get; set; }
}
You can create a mapping between Person and PersonDto like this
Mapper.CreateMap<Person, PersonDto>()
and when you query the database using Entity Framework (for example), you can use something like this to get PersonDto columns only:
ctx.People.Where(p=> p.FamilyName.Contains("John"))
.Project()
.To<PersonDto>()
.ToList();
which will return a list of PersonDtos that has a family name contains "John", and if you run a sql profiler for example you will see that only the PersonDto columns were selected.
Automapper also supports hierachy, if your Person for example has an Address linked to it that you want to return AddressDto for it.
I think it worth to have a look and check it, it cleans a lot of the mess that manual mapping requires.
I thought about it a little, and I didn't come up with any "awesome" solution.
Essentially you have two general choices here,
Use placeholder and rewrite expression tree entirely.
Something like this,
public static Expression<Func<ClassA, DtoA>> DtoExpression{
get{
Expression<Func<ClassA, DtoA>> dtoExpression = classA => new DtoA(){
BDto = Magic.Swap(ClassB.DtoExpression),
};
// todo; here you have access to dtoExpression,
// you need to use expression transformers
// in order to find & replace the Magic.Swap(..) call with the
// actual Expression code(NewExpression),
// Rewriting the expression tree is no easy task,
// but EF will be able to understand it this way.
// the code will be quite tricky, but can be solved
// within ~50-100 lines of code, I expect.
// For that, see ExpressionVisitor.
// As ExpressionVisitor detects the usage of Magic.Swap,
// it has to check the actual expression(ClassB.DtoExpression),
// and rebuild it as MemberInitExpression & NewExpression,
// and the bindings have to be mapped to correct places.
return Magic.Rebuild(dtoExpression);
}
The other way is to start using only Expression class(ditching the LINQ). This way you can write the queries from zero, and reusability will be nice, however, things get harder & you lose type safety. Microsoft has nice reference about dynamic expressions. If you structure everything that way, you can reuse a lot of the functionality. Eg, you define NewExpression and then you can later reuse it, if needed.
The third way is to basically use lambda syntax: .Where, .Select etc.. This gives you definitely better "reusability" rate. It doesn't solve your problem 100%, but it can help you to compose queries a bit better. For example: from.MyCList.Select(dtoCSelector)
I am using MVVM and displaying some items on a DataGrid. My model is RecordingInfo and looks like:
public class RecordingInfo : IDataErrorInfo
{
public RecordingInfo(string fullDirectoryName, string recordingName, int recordingNumber)
{
FullDirectoryName = fullDirectoryName;
RecordingName = recordingName;
RecordingNumber = recordingNumber;
}
public string FullDirectoryName { get; internal set; }
public string RecordingName { get; set; }
public int RecordingNumber { get; internal set; }
public string Error
{
get { throw new NotImplementedException(); }
}
public string this[string propertyName]
{
get {
if (propertyName == "RecordingName")
{
if (this.RecordingName.Length < 2)
return "Recording Name must be at least two characters";
}
return null;
}
}
}
I end up with a collection of these RecordingInfo programmatically. The user is not allowed to do much with these but he/she can change the RecordingName subject to the name being 2 characters or more AND that the RecordingName must be unique. I.e. no changing it to match another RecordingName. I have taken care of the first requirement. It's the second one that is giving me grief.
For my ViewModel, I have
public class RecordingListViewModel : ViewModelBase//, IDataErrorInfo
{
private ObservableCollection<RecordingInfo> _recordings = null;
public RecordingListViewModel()
{
}
public ObservableCollection<RecordingInfo> Recordings
{
get
{
return _recordings;
}
}
// more stuff left off for brevity
In my view I bind the collection to a DataGrid and have:
<DataGrid ItemsSource="{Binding Path=Recordings}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Recording" IsReadOnly="False" EditingElementStyle="{StaticResource CellEditStyle}" ElementStyle="{StaticResource CellNonEditStyle}" Binding="{Binding RecordingName, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" >
</DataGridTextColumn>
...
</DataGrid.Columns>
My way of checking for 2 or more characters works great. But this doesn't work for checking that the user is not trying to give a recording an existing name. Presumably, I need to somehow handle this at the ViewModel layer since the ViewModel knows about all Recordings. I tried playing with having my ViewModel derive from IDataErrorInfo but the property indexer never gets called, which makes sense as it's the Observable collection and therefore the individual RecordingInfos that are bound. I also thought about doing something with a "Lost Focus" event, but DataGridTextColumn doesn't seem to have that. I would think this is a somewhat common problem: validation must take into account relationships between the items of the collection.
By the way, I'm not wedded to the IDataErrorInfo and I am not opposed to other changes in architecture. Please let me know if I can provide more details. I have tried to provide a minimal amount of code. Clearly, this is part of a much bigger project. Any advice is appreciated.
Thanks,
Dave
I would do the following
1) Make RecordingInfo implement INotifyPropertyChanged
2) Use a BindingList<> instead of ObservableCollection<>
In your viewmodel, subscribe to the BindingList.ListChanged Event. This event will fire when items are added and removed, but also when the top level properties on RecordingInfo changes. In the case of a property being changed, the ListChangedEventArgs.PropertyDescriptor property will contain the name of the property, if you want to run validation for just that property (be careful though, this can be null when the item as added/removed). You'll need to use the ListChangedType property to determine the reason of the event (E.x.: Reset means everything changed, ItemAdded means the item was added, but the ItemChanged means a property changed as occurred on an existing item.
You can have the parent ViewModel (that contains and creates your RecordingInfos) pass a name validation Func in their constructors for them to call when validating their name changes.
I am using the example at The Complete Guide To Validation In ASP.NET MVC 3 to create a RequiredIf validation attribute (it's about 1/3 down the page under the heading of "A more complex custom validator"). It all works fine with the exception of one scenario, and that is if I have the need to validate against a complex type. For example, I have the following model:
public class MemberDetailModel
{
public int MemberId { get; set; }
// Other model properties here
public MemberAddressModel HomeAddress { get; set; }
public MemberAddressModel WorkAddress { get; set; }
}
public class MemberAddressModel
{
public bool DontUse { get; set; }
// Other model properties here
[RequiredIf("DontUse", Comparison.IsEqualTo, false)]
public string StreetAddress1 { get; set; }
}
The problem is that when the attribute validation for the StreetAddress property is rendered, it get's decorated with the attribute of data-val-requiredif-other="DontUse". Unfortunately, since the address is a sub-type of the main model, it needs to be decorated with a name of HomeAddress_DontUse and not just DontUse.
Strangely enough, the validation works fine for server-side validation, but client-side unobtrusive validation fails with an JS error because JS can't find the object with a name of just "DontUse".
Therefore, I need to find a way to change the ModelClientValidationRequiredIfRule method to know that the property it is validating is a sub-type of a parent type, and if so, prepend the ParentType_ to the "otherProperty" field (e.g. otherProperty becomes HomeAddress_DontUse.
I have tried passing in typeof(MemberAddressModel) as a parameter of the attribute, but even when debugging the attribute creation, I can't seem to find any reference to the parent type of HomeAddress or WorkAddress from that type.
Based on the suggestion from The Flower Guy, I was able to come up with the following which seems to work. I simply modified the following in the customValidation.js file:
jQuery.validator.addMethod("requiredif", function (value, element, params) {
if ($(element).val() != '') return true;
var prefix = getModelPrefix(element.name); // NEW LINE
var $other = $('#' + prefix + params.other); // MODIFIED LINE
var otherVal = ($other.attr('type').toUpperCase() == "CHECKBOX") ? ($other.attr("checked") ? "true" : "false") : $other.val();
return params.comp == 'isequalto' ? (otherVal != params.value) : (otherVal == params.value);
});
I also added the following method to that file (within the JQuery block so as to be only privately accessible):
function getModelPrefix(fieldName) {
return fieldName.substr(0, fieldName.lastIndexOf(".") + 1).replace(".","_");
}
Cannot do it exactly right now, but the problem is in the client javascript function:
jQuery.validator.addMethod("requiredif" ...
The js is not sophisticated enough to cope with complex view models where there may be a model prefix. If you take a look at Microsoft's jquery.validate.unobstrusive.js (in the Scripts folder over every MVC3 application), you will find some useful methods including getModelPrefix and appendModelPrefix. You can take a similar approach and change the requiredIf validation method - take a look at the equalto method in jquery.validate.unobstrusive.js for a helping hand.
There are certain properties in my application that I need to set dynamically whether they are required or not, therefore I can not use the [Required] atribute of Data Annotations.
Maybe this is not the best way to achieve what I want. So I will accept glady anny suggestions in that regard.
I have overridden the DataAnnotationsModelMetadataProvider with:
public class DynamicFieldsMetadataProvider : DataAnnotationsModelMetadataProvider
{
public override IEnumerable<ModelMetadata> GetMetadataForProperties(object container, Type containerType)
{
if (containerType == null)
throw new ArgumentNullException("containerType");
if (!typeof(DynamicFieldDataItem).IsAssignableFrom(containerType))
foreach (var metadataProperty in base.GetMetadataForProperties(container, containerType))
yield return metadataProperty;
else
foreach (var metadataProperty in base.GetMetadataForProperties(container, containerType))
{
var dynamicField = (DynamicFieldDataItem)container;
if (metadataProperty.PropertyName == "DataFieldValue")
metadataProperty.IsRequired = dynamicField.IsRequired;
yield return metadataProperty;
}
}
}
This is just a concept test, once I make it work, I will change it to something dynamic and more object oriented, but so far, with just being able to set the MetadataModel for the property DataFieldValue to IsRequired = true I can get going.
With this I successfully set in a dynamic way the IsRequired property in true (I thought, with this would be enough!), and when I debugg in my view:
#Html.EditorFor(model=>model.DataFieldValue)
The property DataFieldValue is declared like this:
public class DynamicFieldDataItem
{
public string DataFieldValue { get; set; }
public bool IsRequired{ get; set; }
}
I can see that the Metadata, has the property IsRequired in true, but when the "DataFieldValue" is rendered the "validator" is not there, and of course the validation doesn't work.
To make sure that there wasn't a problem on the configuration of my project, I checked web.config and the inclusion of the javascripts for validation, all is properly configured. What's more, If I add the attribute Required to my property, like this:
public class DynamicFieldDataItem
{
[Required]
public string DataFieldValue { get; set; }
public bool IsRequired{ get; set; }
}
The validation works perfectly!
Can anyone give me a hint? Or tell me what i am doing wrong?
Thanks!
For validations that advanced I recommend you look at FluentValidation for MVC
http://fluentvalidation.codeplex.com/wikipage?title=mvc
you can install it into your project using NuGet, and you can create custom validation classes with it.
I believe you need both a custom ModelMetadataProvider and a custom ModelValidatorProvider for this to work. From my experience they don't seem to leverage each other and both seem to setup different validation.
This might be a stupid question! (n00b to AutoMapper and time-short!)
I want to use AutoMapper to map from EF4 entities to ViewModel classes.
1) If I call
CreateMap<ModelClass, ViewModelClass>()
then do I also need to call
CreateMap<ViewModelClass, ModelClass>()
to perform the reverse?
2) If two classes have the same property names, then do I need a CreateMap statement at all, or is this just for "specific/custom" mappings?
For the info of the people who stumble upon this question. There appears to be now a built-in way to achieve a reverse mapping by adding a .ReverseMap() call at the end of your CreateMap() configuration chain.
In AutoMapper you have a Source type and a Destination type. So you will be able to map between this Source type and Destination type only if you have a corresponding CreateMap. So to answer your questions:
You don't need to define the reverse mapping. You have to do it only if you intend to map back.
Yes, you need to call CreateMap to indicate that those types are mappable otherwise an exception will be thrown when you call Map<TSource, TDest> telling you that a mapping doesn't exist between the source and destination type.
I've used an extension method do mapping both ways
public static IMappingExpression<TDestination, TSource> BothWays<TSource, TDestination>
(this IMappingExpression<TSource, TDestination> mappingExpression)
{
return Mapper.CreateMap<TDestination, TSource>();
}
usage:
CreateMap<Source, Dest>().BothWays();
Yes, or you can call CreateMap<ModelClass, ViewModelClass>().ReverseMap().
If two classes have same Member(Property,Field,GetMethod()), you needn't call CreateMap<TSrc,TDest>. Actually, if every member in TDest are all exist in TSrc, you needn't call CreateMap<TSrc,TDest>. The following code works.
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
class Person2
{
public string Name { get; set; }
public int? Age { get; set; }
public DateTime BirthTime { get; set; }
}
public class NormalProfile : Profile
{
public NormalProfile()
{
//CreateMap<Person2, Person>();//
}
}
var cfg = new MapperConfiguration(c =>
{
c.AddProfile<NormalProfile>();
});
//cfg.AssertConfigurationIsValid();
var mapper = cfg.CreateMapper();
var s3 = mapper.Map<Person>(new Person2 { Name = "Person2" });