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.
Related
I am working with WPF and MVVM, and so have a lot of properties in my view models that are bound to stuff in the view. The majority of these properties look like this...
private DateTime _newRevisionDate = DateTime.Now;
public DateTime NewRevisionDate {
get {
return _newRevisionDate;
}
set {
if (_newRevisionDate != value) {
_newRevisionDate = value;
RaisePropertyChanged(ViewModelUtils.GetPropertyName(() => NewRevisionDate));
}
}
}
I'm using MvvmLight, which is where the RaisePropertyChanged() method comes from, and have used the ViewModelUtils.GetPropertyName() method to create a string from the property name, avoiding the need for magic strings.
Now, the problem is that if I add a few such properties to a view model, I end up with a large amount of almost identical code. This just cries out for some clever refactoring, so I can just use a single line of code to define each property.
However, I haven't been able to find any way to do this yet. What would be nice is to be able to do something like the standard C# automatic properties...
public DateTime NewRevisionDate { get; set; }
...but have it call RaisePropertyChanged() whenever the property is set to a new value.
Anyone any ideas? Thanks
This just cries out for some clever refactoring, so I can just use a single line of code to define each property.
Well you can make it a single line now. It's just a very long line :)
C# 5 makes this slightly easier with caller info attributes, so you don't need the GetPropertyName part - and that's the ugliest part of your current code.
The other thing you could do would be:
set
{
_newRevisionDate = PossiblyFireEvent(RaisePropertyChanged, _newRevisionDate, value);
}
where PossiblyFireEvent would take the property name as an optional parameter using the caller info attributes, RaisePropertyChanged as a delegate to execute if the two values were unequal, and always return value. Not sure it's worth it though.
as I often let LinqToSql generate partial entity classes, I am wondering if my practice of adding additional properties via code is correct and if there is a better way of doing the same thing? I am also wondering what is the difference between accessing the values of other properties using this.PROPERTY_NAME vs _PROPERTY_NAME?
In my web app I keep using this.PROPERTY_NAME, but I am wondering if that is, as I already said in opening sentence, the proper approach I should be using. Also, What is _PROPERTY_NAME and when do we use it?
Example:
public partial class User
{
public bool IsThisProper {
get{
return this.SomeIntProperty == 10; // I usually use this
}
}
public bool WhenToUseThisApproach {
get{
return _SomeIntProperty == 10; // What is this in comparison to above?
}
}
}
One is the property, and the other is the private backing field in which that property stores it's value. If you want to execute whatever code the property has in it's getter/setter, then use the property, if you don't, then don't. Chances are you want to use the property, not the field, especially with setting (setting it triggers the property changed event, so about the only time to use the property is if you don't want that event raised).
I have this class:
public class MyProps
{
public MyProps()
{
}
protected string myVar;
public string MyProperty
{
get { return myVar; }
set { myVar = value; }
}
protected int myOtherVar;
public int MyOtherProperty
{
get { return myOtherVar; }
set { myOtherVar = value; }
}
}
That I want to add to my Form, so when I inherit from it I will be able to fill the properties in the MyPropsX property.
I have this code in my form:
protected MyProps propsX = new MyProps();
[TypeConverter(typeof(ExpandableObjectConverter))]
public MyProps MyPropsX
{
get
{
return propsX;
}
set
{
propsX = value;
}
}
Now, the properties MyProperty and MyOtherProperty are nicely shown in the Properties Window, and I can set their values directly there.
But when I close my form and I open it again, all my changes are lost, the properties being reset to show zero and an empty string.
What am I missing?
Should I inherit my MyProps class from certain special class or interfase?
Or some special attribute?
This is a little bit much for a comment and maybe your solution, so i'm answering to your comment with an answer instead with another comment:
With does not happen when I put properties directly on a form you mean, you are using the designer to set some property of the form. These will be written into the MyForm.designer.cs file. When you go into the code of your class you'll find within the constructor a method InitializeComponent(). Set the cursor on it an press F12. Here you can see what the designer has written into all the properties. You should respect the comment above the mentioned method and not start to modify the code with the code editor unless you really have understand how and when the designer will read and write code here (which is another chapter i can explain if needed). Otherwise it will happen that trying to opening your form with the designer after the code change will lead to an error message or code loss.
If you like to set some default value also, you should go back into the constructor and add the needed initialization code below the InitializeComponent() function and everything should work as expected.
Update
As you wrote in your comment you already know how the Designer interacts with the *.designer.cs file. So i really can't understand your concrete problem but maybe one of these articles can give you a more insight about how Microsoft wrote their components:
Make Your Components Really RAD with Visual Studio .NET Property Browser
Components in Visual Studio
This is very normal, since each time you are closing the form and opening it again you are having a new instance from the form MyPropsX, so the best way would be to save your properties in any kind of a database (sql, access, textfiles,...)
According to Microsoft:
http://msdn.microsoft.com/de-de/library/system.data.linq.mapping.columnattribute.expression.aspx
It's possible to add expression to the Linq-to-SQL Mapping.
But how to configure or add them in Visual Studio in the Designer?
Problem, when I add it manual to thex XYZ.designer.cs it on change it will be lost.
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.4927
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
This is generated:
[Column(Name="id", Storage="_id", DbType="Int")]
public System.Nullable<int> id
{
...
But i need something like this
[Column(Name="id", Storage="_id", DbType="Int", Expression="Max(id)")]
public System.Nullable<int> id
{
...
Thanks.
According to this article:
http://msdn.microsoft.com/en-us/library/system.data.linq.mapping.columnattribute.expression.aspx
you should use the ColumnAttribute.Expression Property when you use CreateDatabase to define a column as containing computed values.
So you should check this article:
http://msdn.microsoft.com/en-us/library/Bb399420%28v=VS.100%29.aspx
Another way is to define expression on your sql server so it'll be mapped by the LINQ designer.
Edit: mmmm you edited your question, so probably my answer is not gonna help you so much, but you might be able to do this anyway with your 'extended' question :D
I do this by adding another class file to the project, give them the same name as the object from LinQ-to-SQL you want to extend and define it as partial.
for example, if you have a table called Files, the object File will be created for you by L2S. If you then create a file (with the same namespace as your DataContext object), and make it partial, like this:
public partial class File
{
}
You can just add properties, methods, etc. From within this class, you also have direct access to the properties of the 'other' File class.
It's a little klugy, but in your linq2sql designer, rename the field from 'id' to 'xid' (or anything else) and change its accessibility to internal.
then, in another file, start another partial class, like Wim Haanstra showed, and create a new property called 'id', add all the attributes you want, and in the get & set, just map it to and from the original property, now called 'xid'.
it would look something like this:
public partial class File
{
public int? id
{
get { return xid; }
set { xid = value; }
}
}
this is more commonly done to map fields in the database to a different type in the object, e.g. an int in the DB to an enum in the object, a byte/smallint/etc. in the DB, a boolean in the object. or to add attributes, like [DataMember] to the property.
I believe there is no human way to change any attribute or field inside an Attribute apart from doing it in the constructor. That is, short of redesigning and recompiling Visual Studio yourself. There is already a similar question posted here:
Change Attribute's parameter at runtime
but I believe the peculiarities of my problem are different enough to require a new post.
I use an enumeration to keep track of the different columns of a DataTable. I use attributes in each enumeration element to indicate the underlying type and the description -in case the .ToString() would give an "ugly" result due to the rigid set of characters that are allowed to name an enumeration element, such as "Tomato_Field" when you want "Tomato Field", and the like. This allows me to place all the related information in the same object, which is, I believe, what it should be. This way I can later create all the columns with a simple and clean foreach that cycles through the elements of the enumeration and extracts the metedata (description and type) to create each column.
Now, some of the columns are autocalculated, which means that during their creation -via DataTable Identifier.Columns.Add.(NameOfColumn,underlyingType,optional: autocalculatedString)- I need to specify a string that determines how it should be calculated. That string must use the names of other columns, which might be in the Description Attribute. The approach that looks logical is to use another attribute that holds the string, which should be built using the names of the other columns, requiring access to the metadata. Now that seems impossible in the constructor: you are forced to provide a constant string. You can't use a method or anything.
This problem could be solved if there were a way to change a property inside the attribute (lets call it AutocalculatedStringAttribute) at runtime. If you access the metadata you can retrieve the string you used at the constructor of the Attribute, and you can of course change that string. However, if you later access the metadata again that change is ignored, I believe the constructor is called every time the metadata is accessed at runtime, thus ignoring any changes.
There are, of course, dirty ways to achive what I am trying to do, but my question is specifically if there is a way to properly use attributes for this. Short of resorting to CodeDOM to recompile the whole assembly with the constructor of the AutocalculatedStringAttribute changed, a certain overkill.
Right, the metadata that's used to initialize the attribute is immutable. But you can add properties and methods to an attribute class that can run code and return relevant info after the attribute object is constructed. The data they rely on doesn't have to be stored in metadata, it can be persisted anywhere.
Of course, such code wouldn't have to be part of the attribute class implementation, it could just as well be part of the code that instantiates the attribute. Which is where it belongs.
It isn't entirely clear to me what code is consuming this attribute, and it matters...
You cannot change an attribute that is burned into the code - you can query it with reflection, but that is about it. However, in many cases you can still do interesting things - I don't know if they apply to your scenario, though:
you can subclass many attributes like [Description], [DisplayName], etc - and while you pass in a constant string (typically a key) to the .ctor, it can return (through regular C#) more flexible values - perhaps looking up the description from a resx to implement i18n
if the caller respects System.ComponentModel, you can attach attributes at runtime to types etc very easily - but much harder on individual properties, especially in the case of DataTable etc (since that has a custom descriptor model via DataView)
you can wrap things and provide your own model via ICustomTypeDescriptor / TypeDescriptionProvider / PropertyDescriptor - lots of work, but provides access to set your own attributes, or return a description (etc) outside of attributes
I don't know how much of this is suitable for your environment (perhaps show some code of what you have and what you want), but it highlights that (re the question title) yes: there are things you can do to tweak how attributes are perceived at runtime.
I wanted to post this as a comment but since I wanted to include some code I couldn't, given the 600 characters limit. This is the cleanest solution I have managed to find, although it does not include all the info to create the columns on the enum, which is my goal. I have translated every field to make it easier to follow. I am not showing some code which has an obvious use (in particular the implementations of the other custom attributes and their static methods to retrieve the metadata, assume that it works).
This gets the job done, but I would ideally like to include the information stored in the strings "instancesXExpString " and "totalInstancesString" in the Autocalculated attribute, which currently only marks the columns that have such a string. This is what I have been unable to do and what, I believe, cannot be easily accomplished via subclassing -although it is an ingenious approach, I must say.
Thanks for the two prompt replies, btw.
And without any further ado, lets get to the code:
// Form in which the DataGridView, its underlying DataTable and hence the enumeration are:
public partial class MainMenu : Form {
(...)
DataTable dt_expTable;
//Enum that should have all the info on its own... but does not:
public enum e_columns {
[TypeAttribute(typeof(int))]
Experiments = 0,
[TypeAttribute(typeof(decimal))]
Probability,
[DescriptionAttribute("Samples / Exp.")]
[TypeAttribute(typeof(int))]
SamplesXExperiment,
[DescriptionAttribute("Instances / Sample")]
[TypeAttribute(typeof(int))]
InstancesXSample,
[DescriptionAttribute("Instances / Exp.")]
[TypeAttribute(typeof(int))]
[Autocalculated()]
InstancesXExp,
[DescriptionAttribute("Total Instances")]
[TypeAttribute(typeof(long))]
[Autocalculated()]
Total_Instances
};
//These are the two strings
string instancesXExpString = "[" + DescriptionAttribute.obtain(e_columns.SamplesXExperiment) + "] * [" + DescriptionAttribute.obtain(e_columns.InstancesXMuestra) + "]";
string totalInstancesString = "[" + DescriptionAttribute.obtain(e_columns.InstancesXExp) + "] * [" + DescriptionAttribute.obtain(e_columns.Experiments) + "]";
public MainMenu() {
InitializeComponent();
(...)
}
private void MainMenu_Load(object sender, EventArgs e) {
(...)
// This is the neat foreach I refered to:
foreach (e_columns en in Enum.GetValues(typeof(e_columnas))) {
addColumnDT(en);
}
}
private void addColumnDT(Enum en) {
//*This is a custom static method for a custom attrib. that simply retrieves the description string or
//the standard .ToString() if there is no such attribute.*/
string s_columnName = DescriptionAttribute.obtain(en);
bool b_typeExists;
string s_calculusString;
Type TypeAttribute = TypeAttribute.obtain(en, out b_typeExists);
if (!b_typeExists) throw (new ArgumentNullException("Type has not been defined for one of the columns."));
if (isCalculatedColumn(DescriptionAttribute.obtain(en))) {
s_calculusString = calcString(en);
dt_expTable.Columns.Add(s_columnName, TypeAttribute, s_calculusString);
} else {
dt_expTable.Columns.Add(s_columnName, TypeAttribute);
}
}
private string calcString(Enum en) {
if (en.ToString() == e_columns.InstancessXExp.ToString()) {
return instancesXExpString;
} else if (en.ToString() == e_columns.Total_Samples.ToString()) {
return totalInstancesString;
} else throw (new ArgumentException("There is a column with the autocalculated attribute whose calculus string has not been considered."));
}
(...)
}
I hope this piece of code clarifies the situation and what I am trying to do.