Inheritance in model causing unintended consequences - c#

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.

Related

Get specific type from derived class

Brief: I'm creating an MVC application in which I need to display a variety of types documents, some containing more author information than others.
What I wanna do: My approach is to have a generic "view document" view, which dynamically displays the document in a format dictated by the shape/type of the object passed to it.
Example: A simple document would be loaded into a SimpleDocumentViewModel, and display as such. However I'd like to load a larger type of document into an ExtendedDocumentViewModel, bringing with it additional information about both the document and the author. The view(s) would then display the appropriate data based on the object it receives.
Where I'm at now: In this vein I've created the following interfaces and classes, but I'm stuck as to how to return/identify the more specific return types in their derived classes.
abstract class BaseDocumentViewModel : DocumentViewModel, IDocumentViewModel
{
public int DocumentId { get; set; }
public string Body { get; set; }
public IAuthorViewModel Author { get; set; }
}
class SimpleDocumentViewModel : BaseDocumentViewModel
{
}
class ExtendedDocumentViewModel : BaseDocumentViewModel
{
public new IAuthorExtendedViewModel Author { get; set; }
}
interface IAuthorViewModel
{
int PersonId { get; set; }
string Name { get; set; }
}
interface IAuthorExtendedViewModel : IAuthorViewModel
{
int ExtraData { get; set; }
int MoreExtraData { get; set; }
}
Question: So my question is; how best can I get the specific types from the fully implemented classes, or do I need to return the base types and query it all in the view? Or am I off my head and need to go back to the drawing board?
Edits:
I know that c# doesn't support return type covarience, but hoped that there may be another way of returning/identifying the derived types so that I don't have to query them all in the view.
My current solution would be to always return the base types, and have a separate view for each concrete type that simply casts each object to the correct type, only querying those that could differ. Perhaps this is the best solution end of, but it feels very inelegant.
Usually you can do a simple "is" check. So you can have conditional rendering in your views, for example:
#if(Model is ExtendedDocumentViewModel)
{
// render ExtendedDocumentViewModel html here
}
Type checking is usually considered an anti pattern, however I am not sure if there is a much better approach to this problem. If you are using .NET Core you can also check the subclass tag here http://examples.aspnetcore.mvc-controls.com/InputExamples/SubClass .
Possible cleaner option is to just have a signature in the interface called GetView that each document has to implement. This way each document type has their own way of implementing the function and the calling function knows that each document has a function GetView. This method will work well if every document has a unique way of viewing the document. However if some documents share the same way of getting views, then may I suggest creating each View type into their own class and you can assign the views types to each document. I suggest looking into the strategy pattern.
First suggestion:
class SimpleDocumentViewModel : IAuthorViewModel
{
view GetView()
{
... do document specific stuff
... return view
}
}
class ExtendedDocumentViewModel : IAuthorViewModel
{
int ExtraData { get; set; }
int MoreExtraData { get; set; }
view GetView()
{
... do document specific stuff
... return view
}
}
interface IAuthorViewModel
{
view GetView();
}
Second suggestion:
class SimpleDocumentViewModel : IAuthorViewModel
{
public viewType1 view {get;set;}
public SimpleDocumentViewModel(viewType1 viewIn,etc...)
{
view = viewIn;
}
view GetView()
{
return view.GetView();
}
}
class ExtendedDocumentViewModel : IAuthorViewModel
{
int ExtraData { get; set; }
int MoreExtraData { get; set; }
public viewType2 view {get;set;}
public ExtendedDocumentViewModel(viewType2 viewIn,etc...)
{
view = viewIn;
}
view GetView()
{
return view.GetView(ExtraData,MoreExtraData);
}
}
interface IAuthorViewModel
{
view GetView();
}
I may be way off base here, but as I understand your question... why not just throw the return types in an object and pass that to your view?
You could look at the desired method and use reflection to pull out whatever info you want. Modify this and the object class hold whatever you want it to.
public class DiscoverInternalClass
{
public List<InternalClassObject> FindClassMethods(Type type)
{
List<InternalClassObject> MethodList = new List<InternalClassObject>();
MethodInfo[] methodInfo = type.GetMethods();
foreach (MethodInfo m in methodInfo)
{
List<string> propTypeList = new List<string>();
List<string> propNameList = new List<string>();
string returntype = m.ReturnType.ToString();
foreach (var x in m.GetParameters())
{
propTypeList.Add(x.ParameterType.Name);
propNameList.Add(x.Name);
}
InternalClassObject ICO = new InternalClassObject(c.Name, propNameList, propTypeList);
MethodList.Add(ICO);
}
return MethodList;
}
}
he object class could be something like this or modify it however you want:
public class InternalClassObject
{
public string Name { get; set; }
public List<string> ParameterNameList { get; set; }
public List<string> ParameterList { get; set; }
public InternalClassObject(string iName,List<string> iParameterNameList, List<string> iParameterList)
{
Name = iName;
ParameterNameList = iParameterNameList;
ParameterList = iParameterList;
}
}
You could call the method like this with the desired class.
public static List<InternalClassObject> MethodList = new List<InternalClassObject>();
DiscoverInternalClass newDiscover= new DiscoverInternalClass();
MethodList = newDiscover.FindClassMethods(typeof(ExtendedDocumentViewModel));
Now you can have your GetView build based on what is in MethodList
Hope this helps!

Does Getter and Setter in ASP.MVC can instantiate an object?

From Microsoft MVC doc, related to Authoring Tag Helpers, I can read this:
using System;
namespace AuthoringTagHelpers.Models
{
public class WebsiteContext
{
public Version Version { get; set; }
public int CopyrightYear { get; set; }
public bool Approved { get; set; }
public int TagsToShow { get; set; }
}
}
and this:
using System;
using AuthoringTagHelpers.Models;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace AuthoringTagHelpers.TagHelpers
{
public class WebsiteInformationTagHelper : TagHelper
{
public WebsiteContext Info { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "section";
output.Content.SetHtmlContent(
$#"<ul><li><strong>Version:</strong> {Info.Version}</li>
<li><strong>Copyright Year:</strong> {Info.CopyrightYear}</li>
<li><strong>Approved:</strong> {Info.Approved}</li>
<li><strong>Number of tags to show:</strong> {Info.TagsToShow}</li></ul>");
output.TagMode = TagMode.StartTagAndEndTag;
}
}
}
I never saw this kind of code before, where public WebsiteContext Info { get; set; } can automagically instantiate an object???
How it works? Is there any documentation on it?
The answer is in the document you linked:
Note
In the Razor markup shown below:
<website-information info="new WebsiteContext {
Version = new Version(1, 3),
CopyrightYear = 1638,
Approved = true,
TagsToShow = 131 }" />
Razor knows the info attribute is a class, not a string, and you want to write C# code. Any non-string tag helper attribute should be written without the # character.
The tag helper itself doesn't know how to instantiate the instance. You have to do it manually in the Razor markup or set it to a default value in the property declaration or class constructor in order for it to be non-null. Here is an example of setting the instance in the property declaration.
public WebsiteContext { get; set; } = new WebSiteContext
{
Version = new Version(1, 3),
CopyrightYear = 1638,
Approved = true,
TagsToShow = 131
};
public WebsiteContext Info { get; set; } is not instantiating anything here. If you call the following code:
var websiteInformationTagHelper = new WebsiteInformationTagHelper();
then websiteInformationTagHelper.Info will be equal to null
Note, that it is now possible in c# to assign default values like the following which is a little bit different than what you are wondering about:
public WebsiteContext Info { get; set; } = new WebsiteContext()
Not automatically, but yes. The get and set keywords are shorthand for methods that are called after the property is accessed (get) or assigned to (set). You can add a body with a regular code block:
get { return _backingField; }
set { _backingField = value; }
The value keyword represents the value being assigned to the property and you can do most things in those blocks, same as any method, including instantiating an object.
Microsoft documentation - Auto implemented properties:
learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/auto-implemented-properties
If you're referring to instantiating the parent object, that I don't believe makes sense.

Save methods and results

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.

Custom DataAnnotation attributes

When I use DisplayAttribute in ASP.NET MVC 3 models it quickly becomes a pain writing them because we have to either hardcode the string or reference the string from a some static class that contains const strings (which is what I have now, see below). But even that is too much for me.
I would like to come up with an attribute that would be called something like [SimpleDisplay] and it would implicitly construct the string for resources by looking at
class name,
property name that the attribute is attached to.
Is this possible?
Something like this
public class Product {
[SimpleDisplay] // it will take Product and Name and do something like this Product_Name
public string Name { get; set; }
}
This is what I want to get rid of, if possible:
[Display(ResourceType = typeof(Resources.Localize), Name = ResourceStrings.product_prettyid)]
public virtual int PrettyId
{
get;
set;
}
[Display(ResourceType = typeof(Resources.Localize), Name = ResourceStrings.product_name)]
public virtual string Title
{
get;
set;
}
Now I know that it is not possible to inherit the DisplayAttribute cause it's sealed. What other options I have? Does it even make sense?
I would try creating just a standard attribute and custom DataAnnotationsModelMetadataProvider. You can override CreateMetadata method, which gets IEnumerable<Attribute>. You should than search for your attribute
attributes.OfType<SimpleDisplayAttribute>().FirstOrDefault();
and populate model metadata in any way you want.
If i have a correct understanding what you mean, you may just create a simple custom attribute like this one:
public class LocalizedDisplayNameAttribute : DisplayNameAttribute {
public LocalizedDisplayNameAttribute(string expression) : base(expression) { }
public override string DisplayName {
get {
try {
string[] vals = base.DisplayName.Split(',');
if(vals != null && vals.Length == 2)
return (string)HttpContext.GetGlobalResourceObject(vals[0].Trim(), vals[1].Trim());
} catch {}
return "{res:" + base.DisplayName + "}";
}
}
}
You may then use it as an attribute on your properies. MVC HTML extensions will pickup your custom attribute.
[LocalizedDisplayName("LBL, lbl_name1")]
public string[] Name1 { get; set; }

IDataErrorInfo with complex types

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.

Categories

Resources