I seem to be running into a weird issue and after hours of head scratching, I seem to have narrowed the issue down to a combination of partial classes and virtual properties. When I override a property that's in a partial class, sitting in a separate file, MVC duplicates the fields on my view. I am using Visual Studio 2013 and the issue can be duplicated by following these steps:
Open Visual Studio and create a new Project. Choose Web under the categories, then choose "ASP.NET Web Application". I am targeting .NET 4.5.
Choose "Empty" from the template selection, then check the MVC checkbox so it adds the core folders and references.
Once the project is created, right-click on the Models folder and create a new class called MyModel.cs.
Add these lines to the new file:
public abstract partial class MyOriginalModel
{
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
}
public partial class MyModel : MyOriginalModel
{
}
Now right click on the Models folder again and create another new class called MyModelCustom.cs.
Add these lines to the file:
public partial class MyModel
{
[System.ComponentModel.DisplayName("First Name")]
[System.ComponentModel.DataAnnotations.Required]
public override string FirstName
{
get
{
return base.FirstName;
}
set
{
base.FirstName = value;
}
}
[System.ComponentModel.DisplayName("Last Name")]
[System.ComponentModel.DataAnnotations.Required]
public override string LastName
{
get
{
return base.LastName;
}
set
{
base.LastName = value;
}
}
}
Now build the project, then right click on the Controllers folder and add a new controller. Choose "MVC 5 Controller with read/write actions" and call it NamesController. Right click on the Create method and go to "Add View". Under the template dropdown, choose Create and for the Model Class dropdown, choose MyModel.
Once MVC creates the template, you will see that it adds First Name and Last Name twice. The issue seems to be related to partial classes because if I move the contents of MyModelCustom.cs into MyModel.cs, everything works fine. However, its not just partial classes. If I create a new property (versus overloading one) in the partial class, it does not duplicate that property. So it seems to be a combination of partial classes and overriding virtual properties.
Can someone please confirm if this is a bug or if I am doing something wrong?
It is a bit of both. Bug or not, if MVC is scaffolding incorrectly, you will either have to constantly fight the framework or change your approach to the problem.
As a general rule, I've found that when you have to fight the MVC framework to make it behave the way you want, then it is far easier to change your approach to the problem. Otherwise, you will end up fighting that particular battle repeatedly until you eventually comply. Take it from someone who's learned that lesson the hard way.
With easier approaches in mind, here are a few things you could try instead:
If you are overwriting a lot of properties, create separate classes with common names for properties (FirstName, LastName). Then use Best way to clone properties of disparate objects to marshall the data between objects.
You could also use Fody PropertyChange listeners to handle whatever logic is needed when these values are changed thereby eliminating the need for the partial overrides entirely.
A final option would be to override the scaffolding templates to skip overridden properties. Not sure how you would detect that though.
Take a look at CodePlex source for MvcScaffolding EnvDTETypeLocator.cs
/// <summary>
/// Out of a set of CodeType instances, some of them may be different partials of the same class.
/// This method filters down such a set so that you get only one partial per class.
/// </summary>
private static List<CodeType> PickArbitraryRepresentativeOfPartialClasses(IEnumerable<CodeType> codeTypes)
{
var representatives = new List<CodeType>();
foreach (var codeType in codeTypes) {
var codeClass2 = codeType as CodeClass2;
if (codeClass2 != null) {
var matchesExistingRepresentative = (from candidate in representatives.OfType<CodeClass2>()
let candidatePartials = candidate.PartialClasses.OfType<CodeClass2>()
where candidatePartials.Contains(codeClass2)
select candidate).Any();
if (!matchesExistingRepresentative)
representatives.Add(codeType);
} else {
// Can't have partials because it's not a CodeClass2, so it can't clash with others
representatives.Add(codeType);
}
}
return representatives;
}
}
:
:
1) PickArbitraryRepresentativeOfPartialClasses, the method uses Linq any() to confirm that the codeType as CodeClass2 has members.
CodeClass2 is the partial class type of EnvDTE, Visual Studio's core Automation library responsible for IDE code generation (Design Time Reflection).
2) If the class cast as CodeClass2 does have members, the class is added to the representatives
3) When the partial class is evaluated, each file will be visited within a distinct context (often leading to a consolidation of elements that should be overridden)
An interesting distinction between Run Time Reflection and Design Time Reflection: sic
An ASP.NET control has two distinct sets of functionality for when it is executed at run-time inside a page or used at design-time inside a host designer. Run-time capabilities determine, based on configuration, the markup that is output by the control. The design-time capabilities, instead, benefit of a visual designer such as Microsoft Visual Studio 2005. Design-time capabilities let the page author configure the control for run-time in a declarative and WYSIWYG (what-you-see-is-what-you-get) manner.
Conclusion:
MVC Scaffolding does use reflection, but it is the much less reliable Design Time Reflection.
Design Time Reflection is not the same as Run Time reflection. A fully compiled Class is a final result of inheritance resolves and partials combined and priority resolved. Design Time Reflection makes best guesses about how to work with complex, multi-part types.
If you want to rely on Scaffolding, better not to push it's limits. When you get errors like this, try simplifying your ViewModels:
Try consolidating your partial classes
Try removing your abstracts / virtuals
Related
We have a web application using ASP.NET MVC, which supports controller methods that take a class object as a parameter. There is automatic binding of the posted form values in order to construct and populate the class object, which occurs before the actual code of the controller method is even invoked. So far so good, but here is my problem: in the course of constructing the class object, the ASP.NET binding engine is invoking every public property of the class. Some of these properties involve some expensive calculation (iterating internal lists, doing counts and so forth), and it is irritating to have them called for no reason whatsoever and the values thrown away. These are read-only properties with only a 'get' and no 'set', so the binder cannot possibly be touching them for purposes of assigning values to them. How can we keep this from happening?
Here is what has been tried so far without success:
Use a [Bind(Include = "...")] in the controller method declaration to limit to the other (non-read-only) properties that can actually be assigned to.
Use a [BindNever] annotation on the read-only properties within the class definition.
Our current workaround is, we just abandon the read-only property implementation altogether and rewrite them all as methods. The binder code does not invoke methods, so this works, but we would still rather have them as properties, and it seems like a problem that should be capable of solution. Any thoughts anyone?
== EDIT =============
Additional things tried, in response to answers here, that still did not work:
Use a [Bind(Exclude = "...")] in the controller method declaration specifying the properties we do not want to invoke. (They are still invoked anyway.)
== EDIT 2 =============
Additional details per request. I am using VS 2015, .NET Framework 4.5.2. Just now I created a sample program to demonstrate the problem:
File -> New -> Project -> Web -> ASP.NET Web Application
Under "ASP.NET 4.5.2 Templates", choose "MVC"
In ManageViewModels.cs, there is a class called "AddPhoneNumberViewModel". This class occurs as a parameter of the method ManageController.AddPhoneNumber (HttpPost version). Add a public property to the class called "PropertyThatShouldNeverBeCalled" and place a breakpoint within it (see code sample below).
Compile and run the application in Debug mode. Attempt to access the endpoint /Manage/AddPhoneNumber, you will have to create an account, then access the endpoint again, enter a phone number, and click "Submit".
Observe that you have hit your breakpoint in PropertyThatShouldNeverBeCalled.
Try one of the unsuccessful fixes described above (e.g. add [Bind(Exclude="PropertyThatShouldNeverBeCalled")] to the definition of ManageController.AddPhoneNumber).
Repeat step 4 above. Observe that you have still hit your breakpoint.
Code sample 1 (from ManageViewModel.cs):
public class AddPhoneNumberViewModel
{
[Required]
[Phone]
[Display(Name = "Phone Number")]
public string Number { get; set; }
public bool PropertyThatShouldNeverBeCalled
{
get
{
bool returnVal = true; // place breakpoint here
return returnVal;
}
}
}
Code sample 2 (from ManageController.cs):
//
// POST: /Manage/AddPhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> AddPhoneNumber([Bind(Exclude = "PropertyThatShouldNeverBeCalled")]AddPhoneNumberViewModel model)
{
// etc.
}
P.S. The problem also occurs when the project is compiled in release mode and run without debugging. I have used logging to confirm this in my original project. For purposes of the sample project, it is just simpler to observe the problem using a breakpoint.
You should use a POCO model such as a DTO (Domain Transfer Object) or a View Model that only contains the properties you need to bind (with any validation you want to have ASP.NET handle as well) and then feed that model into the rest of your application to consume as needed.
You can use the [Bind(Exclude="...")] attribute on the object type as the parameter to the Controller:
Example:
[HttpPost]
public ActionResult Post([Bind(Exclude = "PropertyName")] MyClass dataIn)
{
...
}
MyClass being the object that is being bound on the data coming in to the Controller Action. The Exclude= accepts a comma delimited string for all properties you do not want to be part of the model binding.
https://msdn.microsoft.com/en-us/library/system.web.mvc.bindattribute.exclude(v=vs.118).aspx
I have an N tier app in which Data, Domain and the front-end layers are in separate projects. I am using ASP.NET MVC to create the website and I am trying to add validation rules by using System.ComponentModel.DataAnnotations. Currently I have done it on the domain classes' properties.
I would like to know whether it is a good practice to apply the validation rules directly to the Domain classes? Or it is better to create ViewModels classes in the ASP.NET app and apply the validation rules to the properties of the ViewModel classes?
Hope this question fits here
I appreciate any help.
ViewModel is much better, because ViewModel should understand if it gets valid input from the user. And then you can fix all other exception during convertion using AutoMapper. I would also create a lot of custom , DataAnnotations, DataTypes, Editors, ModeMetaDataRules, and ModelBinder to go with app.
Here is parts of code for ModelFilter using custom ModelBuilder, I am going post part of it, because there is a lot of code involved, but it should get you on right track.
public interface IModelMetadataFilter
{
void TransformMetadata(ModelMetadata metadata,
IEnumerable<Attribute> attributes);
}
public class MultilineTextByNameConvention : IModelMetadataFilter
{
public void TransformMetadata(ModelMetadata metadata, IEnumerable<Attribute> attributes)
{
if ( !string.IsNullOrEmpty(metadata.PropertyName) &&
string.IsNullOrEmpty(metadata.DataTypeName) )
{
if ( metadata.PropertyName.ToLower().Contains("notes")
|| metadata.PropertyName.ToLower().Contains("description")
|| metadata.PropertyName.ToLower().Contains("comment")
)
{
metadata.DataTypeName = DataType.MultilineText.ToString();
}
}
}
}
This code looks for every ViewModel that has property name containing words 'notes', 'description', and 'comment', automatically applying Multitext DataType attribute for all that properties. This type of code can be used for a lot of other different situations. For example fields like SSN can have particular format using RexExpr DataAnnotation, and so on...
You can set attributes on partial classes of your entities and your auto generated classes will not get overridden.
For instance,
Let's say you have entity TheEntity
In a separate file with the Same namespace you can write:
namespace SameNamespaceAsEntities
{
internal sealed class TheEntityMetadata
{
//AStringInTheEntity appears twice in your project
//once in the auto gen file, and once here
[Required(ErrorMessage = "Field is required.")]
public string AStringInTheEntity{ get; set; }
}
//http://stackoverflow.com/questions/14059455/adding-validation-attributes-with-an-entity-framework-data-model
[System.ComponentModel.DataAnnotations.MetadataType(typeof(TheEntityMetadata))]
public partial class TheEntity : IEntity //you can set contracts
{
In an old WPF project I have a class with Properties like this:
private string _name = "";
public string Name
{
get { return _name; }
set
{
string cleanName = clsStringManip.CleanText(value, true);
if (cleanName != _name)
{
_name = cleanName;
}
}
}
Where every time the name changes, I ensure that the value is "cleaned". Putting it in the property ensures I never forget to clean the string before setting the property on the object.
Now I am recreating this system using MVC5 and EntityFramework6.1 using DatabaseFirst.
So all the properties are autogenerated by EF. How then can I add the equivalent CleanText function to my properties without editing the autogen code? - as I'll lose these changes next time I change my database and resync.
All I can find via Google is a way add data annotations via MetadataType and partial classes but this doesn't answer my question.
I tried to add the above code into a partial class but get the error:
The type XXX already contains a definition for Name
The only way I can think is to create a bunch of SetProperty() functions but this is dirty and you can never ensure other developers (or myself) will remember to use them.
Disclaimer: I haven't used EF 6 yet.
Let me answer this in two parts. First, I will tell you how to do this. Then I will tell you why I don't think you should do this. :-)
HOW:
As you discovered, you cannot create another Name property. You need to modify the way the EF generates the code, so that it gives you a place to insert your new code. Depending on how you are using the EF, it often generates Validate() method calls or OnPropertyChanged() calls. You may be able to do what you want inside of those methods.
If you can't do this in Validate() or OnPropertyChanged(), you could change the T4 template to generate something like this:
private string _name = "";
public string Name
{
get { return _name; }
set
{
string cleanName = value;
Cleanup_Name(ref cleanName);
if (cleanName != _name)
{
_name = cleanName;
}
}
}
private partial void Cleanup_Name(ref string);
This gives you a partial method that you can then implement as you see fit. So for any property you want to customize, you can now add another file to your project that does this:
public partial class MyEntity {
void Cleanup_Name(ref string name)
{
// Put your logic in here to fixup the name
}
}
If you do not write the above code block, then the partial method is simply a no-op. (Partial methods must return void, hence the use of a ref parameter).
WHY NOT?
The advantage of this method is that it is totally transparent to the developer. The property is just magically changed. But there are several disadvantages:
Some controls expect that if they call name = "123" that if they get the name back, it is "123" and will fail if this happens. Values are changing but no PropertyChanged event fired. If you do fire the PropertyChanged, then they sometimes change the value back. This can cause infinite loops.
There is no feedback to the user. They typed in one thing, and it looked right, but now it says something different. Some controls might show the change and others won't.
There is no feedback to the developer. The watch window will seemingly change values. And it is not obvious where to see the validation rules.
The entity-framework itself uses these methods when it loads data from the database. So if the database already contains values that don't match the cleanup rules, it will clean them when loading from the database. This can make LINQ queries misbehave depending on what logic is run on the SQL server and what logic is run in the C# code. The SQL code will see one value, the C# will see another.
You might also want to look into what the Entity-Framework's change tracking does in this case. If a property set does a cleanup while loading values from the database, does it consider that a change to the entity? Will a .Save() call write it back to the database? Could this cause code that never intended to change the database to suddenly do so?
ALTERNATIVE
Instead of doing this, I suggest creating a Validate() method that looks at each property and returns errors indicating what is wrong. You could also even create a Cleanup() method that fixes the things that are wrong. This means the cleanups are no longer transparent, so the developer must call them explicitly. But that is a good thing: the code isn't changing values without them realizing it. The person writing the business logic or the UI knows at what point the values will change, and can get a list of why.
The only way you can achieve this is by creating a new property you actually use in your application. Perhaps you can hide the original property in the designer. The actual property you use could look like this:
public string ExternalName
{
get { return Name; }
set
{
string cleanName = clsStringManip.CleanText(value, true);
if (cleanName != Name)
{
Name = cleanName;
}
}
}
As an alternative, you can use POCO classes:
If you want to keep using database-first, check this answer
Use code-first for an existing database, see this detailed guide
Add partial to the generated class.
Change the scope of Name in the generated class from public to internal.
Add the following in the same assembly:
public partial class classname
{
[NotMapped]
public string CleanName
{
get { return Name; }
set
{
var cleanName = clsStringManip.CleanText(value, true);
if (cleanName != Name)
Name = cleanName;
}
}
}
Caveat: you'd have to remember to do steps 1-2 every time you regenerated your POCOs ... I'd seriously consider Code First to Existing Database.
EDIT
Optionally:
Rename Name as InternalName in the generated classname; decorate it with [Column("Name")].
Rename CleanName as Name in the partial class under your control.
Caveat in 4 becomes "remember to do steps 1, 2, and 5 every time you regenerate POCOs".
This approach has the added benefit of not having to modify any of your client code (i.e., use of Name remains Name). And I'd still strongly consider Code First to Existing Database.
In my MonoDevelop project I have an iPhone app. I have two different views. Each view contains a UITable. For view 1 I have class 1 hooked to the UITable as Datasource 1. For View 2 I have a class 2 hooked up as Datasource 2. Both classes (i.e. Datasources) feed the tables with data. View 2 also has a custom cell and because of this loads asynchronous.
I get the data from 2 XML files using linq to XML. Everything works and the data loads great. What I need to do know is to load data in Datasource 2 based on the selection made in View 1. To do this I need to pass an ID from view 1 to Class(Datasource) 2. Problem.
I have tried just about everything I know but I just can't get it right.
The correct solution according to me:
I have created another class called SelectedRound with two properties. Code:
using System;
namespace xxxxx
{
public class SelectedRound
{
public string RoundID { get; set; }
public string Date { get; set; }
}
}
When I set RoundID in class 1 then I can access it in class 1. Trying to access it in class 2 however, returns nothing or null. Why would this happen? Could it be because Class(Datasource) 2 is loading asynchronously? Should I instantiate the SelectedRound class in some global way? If so how? AppDelegate maybe? (I am struggling to do that as well).
It seems pointless to me that setting and getting a simple string variable is difficult.
This feels like it is all about how you are passing the SelectedRound instance from the first view to the second.
As a very quick and dirty solution you could just use a singleton or could just use a static class:
public static class SelectedRound
{
public static string RoundID {get;set;}
public static string Date {get;set;}
}
For a more sophisticated pattern, then try overriding the constructors of one or both of your two view controllers in order to pass them a shared instance of your non-static class.
The view controllers may feel foreign to you right now - but they are just c# classes - so feel free to extend them by writing overrides, new methods and properties.
Our system complexity has risen to the point that we need to make permission names tied to the client from the database more specific. In the client, permissions are referenced from a static class since a lot of client functionality is dependent on the permissions each user has and the roles have a ton of variety. I've referenced this post as an example, but I'm looking for a more specific use case. Take for instance this reference, where PermissionAlpha would be a const string:
return HasPermission(PermissionNames.PermissionAlpha);
Which is great, except now that things are growing more complex the classes are being structured like this:
public static class PermissionNames
{
public static class PermissionAlpha
{
public const string SubPermission = "PermissionAlpha.SubPermission";
}
}
I'm trying to find an easy way to reference PermissionAlpha in this new setup that will act similar to the first declaration above. Would the only way to do this be to resort to pulling the value of the class name like in the example below? I'm trying to keep all the names in one place that can be reference anywhere in the application.
public static class PermissionAlpha
{
public static string Name { get { return typeof(PermissionAlpha).Name; } }
}
** Edit ** - Added missing permission name.
Maybe this would be too big of a change for you with the size of your project, but we have all of our business objects split into partial classes. One is for manual changes and one gets generated. During code-generation, we write the permission keys into the generated side of the partial classes from our "single source of truth". We're using a set of classes as our source of truth and CodeDom to generate, but you could also use a database as your source and use T4, CodeSmith, or others to generate.
Why not create reflectable attribute(s) on the classes in question? That way one can add all the extra information required. I provide a way of divining attributes on my blog article entitled:
C# Using Extended Attribute Information on Objects
HTH