I have an Address object defined simply as follows:
public class Address
{
public string StreetNumber { get; set; }
public string StreetAddress { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
}
Fairly simple. On the advice an answer to another question I asked, I am referring to this blog post when databinding my UI to an object of type Person (which contains an Address MailingAddress field).
The problem is that the IDataError interface method isn't validating any of the properties of the Address type.
public string this[string columnName]
{
get
{
string result = null;
// the following works fine
if(columnName == "FirstName")
{
if (string.IsNullOrEmpty(this.FirstName))
result = "First name cannot be blank.";
}
// the following does not run
// mostly because I don't know what the columnName should be
else if (columnName == "NotSureWhatToPutHere")
{
if (!Util.IsValidPostalCode(this.MailingAddress.PostalCode))
result = "Postal code is not in a know format.";
}
return result;
}
}
So, obviously I don't know what the columnName will be... I've stepped through it and it has never been anything other than any of the public properties (of intrinsic types). I've even tried running and breaking on a statement like:
if (columnName.Contains("Mailing") || columnName.Contains("Postal"))
System.Windows.Forms.MessageBox.Show(columnName);
All to no avail.
Is there something I'm missing?
You need to define IErrorInfo on all the classes that you want to supply error messages for.
Take a look at my answer here.
This explains how to use a modelbinder to add 'class-level' checking of your model without having to use IDataError - which as you have seen here can be quite clumsy. It still lets you use [Required] attributes or any other custom validation attributes you have, but lets you add or remove individual model errors. For more on how to use data annotations I highly recommend this post from Scott Gu.
Related
I am wondering what is a "good practice" for setting properties that are not mapped in the database. I am working with EF Core, but this is really more of a question of design. Imagine I have a following class:
class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
[NotMapped]
public string Name { get; set; }
...
}
Where Name would be computed after fetching the the FirstName and LastName from the database. I am trying to prevent calling the getter for Name without setting the property first.
I know I could do something like
class User
{
...
private string _name = null;
[NotMapped]
public string Name {
get
{
if (_name == null)
_name = $"{FirstName} {LastName}";
return _name;
}
}
...
}
But this gets really messy when multiple properties need to be set this way and when they are obtained in a more complicated way than this one-liner.
For now I wrote a function SetAllProperties() which sets all NotMapped properties, however I don't consider it a good solution since there is a risk of forgetting
To update it when new NotMapped properties are added
To actually call it before accessing the properties
Is an interface a way to go? How should it look like? Or is there some "standard" way of dealing with this? I wasn't able to find anything useful on this topic.
I'm currently working on a content editor that can be used for multiple types of content, where a developer could specify their own model. For example, a model might look like this:
public class ImageWithCopyWidgetModel : WidgetModel, IWidgetModel
{
public string ImageUrl { get; set; }
public string ImageAltText { get; set; }
public string HeaderText { get; set; }
public string BodyContent { get; set; }
}
On the editor side, I have a view model that looks like:
public class EditContentViewModel<TModel> where TModel : IWidgetModel
{
public int Id { get; set; }
public string Name { get; set; }
public TModel WidgetModel { get; set; }
}
I have the binding on the GET/form display side working fine. My issue comes with getting the model binder to accept the data on the POST? I've tried the following, but each returns null for model.WidgetModel:
// Option 1
EditContent(int pageId, int id, EditContentViewModel<dynamic> model)
// Option 2
EditContent(int pageId, int id, EditContentViewModel<object> model)
// Option 3
EditContent(int pageId, int id, EditContentViewModel<IWidgetModel> model)
Note, for testing purposes, I tried explicitly setting the type of WidgetModel to a concrete class (the ImageWithCopyWidgetModel noted above) and that works.
I'm really trying to avoid having to use Request.Form here as its going to limit future plans for this implementation.
What you're wanting is not possible, at least out of the box. On post, all the modelbinder has is a bunch of key-value pair string. What informs its decision about how to bind those values to something useful is the action param(s). Specifically, it has no way of knowing that it should actually create an instance of ImageWithCopyWidgetModel when you're binding to EditContentViewModel.
Also, the modelbinder is designed to discard values it doesn't know what to do with. That means that it's unfortunately not even possible to cast to ImageWithCopyWidgetModel after the fact, because all properties not present on EditCopyViewModel would have been discarded by that point.
Your best bet is a custom model binder, but the implementation of that is too broad for the scope of Stack Overflow. I suggest you refer to the documentation.
This is my model:
public class ManageEngineSite
{
public string accountName { get; set; }
public string siteName { get; set; }
public string description { get; set; }
}
public class ManageEngineUpdateSite : ManageEngineSite
{
public string oldSiteName { get; set; }
}
When I instantiate a new object based on the ManageEngineUpdateSite model:
ManageEngineUpdateSite sitetoUpdate = new ManageEngineUpdateSite();
siteToUpdate.accountName = test1;
siteToUpdate.description = "test description";
siteToUpdate.siteName = "Site Name";
siteToUpdate.oldSiteName = "Old Site Name";
Operation responseModel = Test.UpdateSite(siteToUpdate);
If I set a breakpoint at the last line of code and inspect the object, it has an object modeled like this:
I believe this is breaking my code as the object that is getting passed is seemingly missing oldSiteName.
How do I use inheritance on my model but still resolve this problem?
Actually, you don't have a problem. As shown in your snapshot, your object DOES have the property oldSiteName. It's jsut shown in the debugger in a slightly different place to remind you that it's a property of the ManageEngineUpdateSiteobject, and not an inherited property.
Now, maybe your Test.UpdateSite method accepts a ManageEngineSite as its argument. To access the property, you will have to do a cast.
var siteToUpdateAsManageEngineUpdateSite = siteToUpdate as ManageEngineUpdateSite;
if (siteToUpdateAsManageEngineUpdateSite != null)
{
var oldsiteName = siteToUpdateAsManageEngineUpdateSite.oldsiteName;
// Do things
...
}
That's not a really clean way of doing it though, maybe you should add an override to Test.UpdateSite.
UpdateSite(ManageEngineUpdateSite siteToUpdate)
{
// Do generic things
UpdateSite((ManageEngineSite) siteToUpdate);
var oldsiteName = siteToUpdate.oldsiteName;
// Do specific things
...
}
It's pretty hard to tell with so little info about what you're trying to do.
I have an ASP.Net MVC 5 (C#) application and I'm giving my users the ability to like posts and comments.
for this I have a Model called Likes with the following Properties:
public class Like
{
public Like()
{
this.CreatedUTC = System.DateTime.UtcNow;
this.isActive = true;
}
public long id { get; set; }
public string UserID { get; set; }
public bool isActive { get; set; }
public LikeType Type { get; set; }
public DateTime CreatedUTC { get; set; }
}
Type is an enum and it can be either Comments or Posts. I've also added the following Navigation Property to both Post Model and Comment Model:
public virtual ICollection<Like> Likes { get; set; }
My question is, can I have a setter function in the above line of code where it would automatically set Comments and Posts for the Like type? I know I can use the Value variable in the Setter but using Google I couldn't find how to use it for complex types as I have above (Like).
I'm sure this would be a better way of doing this than in the repository manually set that enum every-time I'm going to save a like.
UPDATE:
Seeing how we're starting a nice little conversation about this Questions, I will throw another alternative in the mix.
How about two Tables with a base class of Like, one CommentLikes and the other PostLikes Seeing that the size of this table will grow really fast, I'm thinking separating them would possibly be a good thing, Right?
I'd rather drop the "LikeType" and either have Comment and Post entity in the Like entity and distinguish by one of them being null or introduce two new entities
public class CommentLike
{
public Comment Comment { get; set; }
public Like Like { get; set; }
}
which holds a comment and a like and PostLike which holds a Post and a Like. The Comment then looks like this
public Comment
{
public virtual ICollection<CommentLike> { get; set; }
}
Another alternative is to create separate tables for comment and post likes. While what you ask for is definitely doable I would recommend more verbose but simpler solution which will result in code that is easier to maintain and has less bugs. Is there any specific reason you want to have a LikeType property?
I had same problem but didnt encounter an easy way.
class Post{
public virtual ICollection<Like> Likes {set;get;}
}
class Comment{
public virtual ICollection<Like> Likes {set;get;}
}
Then:
class Like{
//....
}
You dont need a bi-directional relationship. Do you have a case where you need to query Likes table? and if you do, you will have to manage parsing it as ENUM somewhere which can be an extension method.
EF will create Post_Id and Comment_Id implicityly in your table design. You wont be able to query it but you wont need to. IN my experience i never needed to.
My question is, can I have a setter function in the above line of code
where it would automatically set Comments and Posts for the Like type?
I assume you are using T4 template or the classes that were generated by EF are partialthen you can extend it by creating another partial class and a wrapper property for Likes
// Backing field
private ICollection<Like> _likesWrapper;
public ICollection<Like> LikesWrapper {
get {
// Lazy loading
if(_likes == null)
{
// Just create a new list or load it from the database.
_likes = new List<Like>();
}
return _likes;
}
set {
foreach(var val in value)
{
//Set the comment and posts
}
Likes = value;
}
I currently have a method to save a user.
public PersonDto Save(PersonDto personDto)
However, if, while saving, I find there is a duplicate username, or some other issue with the data - my only way to respond to this is to throw an exception.
throw new Exception("Username exists");
I have read that exceptions shouldn't be used for 'Business Requirement Transgressions'.
Is there a better way to return results to my calling methods? I need to return to PersonDto, but also, some information about any issues. Is there a common practice or model for doing this? Maybe return a 'SaveResult' object, which contains a Object SavedObject (in this case, my PersonDto), as well as some other properties like string SaveResult and bool Success?
Maybe implement a generic wrapper for use with all such requests.
enum Disposition
{
OK,
Warning,
Error
}
class Response<T>
{
public T Result { get; set; }
public Disposition Disposition { get; set; }
public string Message { get; set; }
}
i.e.:
public Response<PersonDto> Save(PersonDto personDto)
This way you can specify some metadata for each of your return values.
I recommend using a SaveResult approach to your problem e.g.
public class SaveResult
{
public PersonDto { get; set; }
public bool Success { get; set; }
public string ErrorMessage { get; set; }
}
var result = Save(person);
if (!result.Success)
{
Console.WriteLine(result.ErrorMessage);
}
Or something like that. This will allow you to pass back the PersonDto, but also signal to the API caller that an error occurred when trying to save.
There's nothing really wrong with throwing an exception, but as you also want to return the person DTO, then you might as well use a specific method return type for that purpose.