Let's say I created a few models via Entity Framework, and one of them is called Paper_Results. This is how the class might look:
public partial class Paper_Results
{
public string Color { get; set; }
public int Height { get; set; }
public int Width { get; set; }
}
I want to use this class like a domain model. Now let's say I create a class the derives from Paper_Results with an added interface
public class Construction_Paper : Paper_Results, IMeasurementType
{
[Required]
public (new?) string Color { get; set; }
[Required]
[Range(1, Int32.MaxValue, ErrorMessage = "Value should be greater than or equal to 1")]
public (new?) int Height { get; set; }
[Required]
[Range(1, Int32.MaxValue, ErrorMessage = "Value should be greater than or equal to 1")]
public (new?) int Width { get; set; }
public virtual string MeasurementType
{
get { return "inches"; }
}
}
Now when I create my ViewModel, I'll used the derived class instead:
public class Construction_Paper_ViewModel
{
Construction_Paper cp;
List<Construction_Paper> cpList;
string title;
public Construction_Paper_ViewModel()
{
title = "Construction Paper";
cp = new Construction_Paper();
cpList = new List<Construction_Paper>();
}
}
I know I should be using uint instead of int for non-negative integers, but I just wanted to add more data annotations to the code. What I'm asking is what is the best OOP technique to derive from Paper_Result class, so that I don't have to modify it at all. The reason is because if I create a new solution and/or project, I don't want to do any modifications to it when I auto-regenerate it using Entity Framework. Should I use shadowing? Or the new keyword in the derived class? Or do you guys have any other better ideas?
The auto-generated EF models do not contain 'virtual' in their methods, thus the reason why I brought up shadowing and the new keyword.
First of all, not every problem should be addressed via inheritance.
Second, the .NET framework already has a mechanism to add metadata (attributes) to an existing object. These are called Buddy Classes, and use the MetadataTypeAttribute class.
The idea is that you add an attribute to the class that allows you to specify a different class that is used to define the metadata for the original class. It's not pretty, but it gets the job done.
Related
Hey … I am facing a pratical code challenge in my project.
Let's say I have a Model class has a property called Configration. The Model object has different type of configration. For example, The Model was made of by Two Pipe objects, then one Lock object and another ten Pipe objects and they have to be in sequence.
In my project, all entity classes are derived from an Entity class that only has Name and ID property.
Model.cs
class Model{
public int ID { get; set; }
public List<ConfigrationEntry<Entity>> Config {get;set;}
}
class Pipe{
public int Length { get; set; }
}
class Lock{
public string Brand { get; set; }
}
So I create a ConfigrationEntry class that contains generic parameter.
class ConfigrationEntry<T> where T : Entity{
public int Number { get; set; }
public T Entity {get; set;}
public ConfigrationEntry(string name, int num){
Entity = new T (name);
Number = num;
}
}
However, if I want to add this ConfigrationEntry into Model.Config List. I won't allow me to do that.
class Main{
var configEntry1 = new ConfigrationEntry<Pipe>("Test1", 10);
var configEntry2 = new ConfigrationEntry<Lock>("Test2", 3);
var configEntry3 = new ConfigrationEntry<Pipe>("Test3", 3);
var Model = new Model();
// I cannot add this entry. IDE says it has to be ConfigrationEntry<Entity> type even Entity is the base class.
// model.Config.Add(configEntry1);
}
So what's the best practical solution for this scenario? Right now I am thinking two use Dictionary<string, int> and use string to find the object.
Yes you're right. You try to use covariant or contravariant (the Entity property is get; set;) and that's not working that way.
In your case you need to add an interface or base class that handles the Entity as base type and add those to the List<>.
Btw: There is a typo in ConfigrationEntry.
Change your configuration entry class to:
interface IConfigurationEntry
{
int Number { get; set; } // is set required?
Entity Entity { get; set; } // is set required?
}
class ConfigurationEntry<T> : IConfigurationEntry
where T : Entity
{
public int Number { get; set; }
public T Entity {get; set;}
Entity IConfigurationEntry.Entity
{
get => this.Entity;
set => this.Entity = (T)value;
}
public ConfigurationEntry(string name, int num){
Entity = new T (name); // this looks strange!
Number = num;
}
}
After having an interface for all ConfigurationEntry<T> instances you're able to change your model:
class Model{
public int ID { get; set; }
public List<IConfigurationEntry> Config {get;set;}
}
If you're using serialization for that Model you need to serialize any type descriptor to deserialize correct instances - but that's another topic and depends on which serialization is used.
I have an object with multiple properties like this :
public int num1 { get; set; }
public int num2 { get; set; }
public string str1 { get; set; }
public string str2 { get; set; }
These properties are inside a class which is dynamically generated so it will erase all CustomAttributes on them.
I tried adding
[Submit]
MyClass myObject
but it didn't spread on my object properties
Is there a way to do that dynamically inside the c# ?
I'm having some trouble understanding your question, but let me attempt to clarify.
I have an object with multiple attributes
...
it will erase all CustomAttributes
In C# parlance, class members of the format <access> <type> <name> { get; set; } are referred to as "properties" rather than "attributes." "Attributes," on the other hand, are the C# implementation of annotations, such as the custom attributes to which you are referring.
That said, I currently understand you to mean you have an automatically generated class with multiple properties. You would like each of these properties to have their own custom attributes, but if you edit the class they are removed the next time it is generated, and you cannot get the class generator to include custom attributes.
It might be helpful to know more of the context of your class. How is it being generated, for example? If it is an Entity Framework class, the following SO question may provide some insight:
Add data annotations to a class generated by entity framework. In general, is (or can you make) the generated class partial? If so, then you can still follow the approach in the above question's answer, viz. make your own partial class implementation that provides the properties' custom attributes.
For example, if your generated class looks (or can be made to look) like this:
/// <auto-generated />
public partial class MyClass
{
public int Num1 { get; set; }
public int Num2 { get; set; }
public string Str1 { get; set; }
public string Str2 { get; set; }
}
You could write the other part of the partial class with the custom annotations, like this:
/// human generated
public partial class MyClass
{
[Submit]
public int Num1 { get; set; }
[Submit]
public int Num2 { get; set; }
[Submit]
public string Str1 { get; set; }
[Submit]
public string Str2 { get; set; }
}
Again, without knowing more about your situation, I am not certain if this provides you the information you need, but I hope it at least gives you a starting point.
Edit
If the class is not partial, you might consider wrapping your generated class with a class whose wrapping properties use the custom attribute. For example,
/// human generated
public class MyClassWrapper
{
private readonly MyClass wrapped;
public MyClassWrapper(MyClass wrapped)
{
this.wrapped = wrapped;
}
[Submit]
public int Num1 { get => this.wrapped.Num1; set => this.wrapped.Num1 = value; }
[Submit]
public int Num2 { get => this.wrapped.Num2; set => this.wrapped.Num2 = value; }
[Submit]
public string Str1 { get => this.wrapped.Str1; set => this.wrapped.Str1 = value; }
[Submit]
public string Str2 { get => this.wrapped.Str2; set => this.wrapped.Str2 = value; }
}
Edit 2
If you would rather have a more dynamic solution, at the cost of some design and runtime complexity, you might consider this SO question: How to add property-level Attribute to the TypeDescriptor at runtime?. It seems to address a similar concern --
Really, it's for MS's Application Settings that generates code, so you can't extend it in any way property-wise.
I won't duplicate Gman's explanation entirely here, but essentially this approach consists of
Get the type (MyClass) or an instance of the type myObject
Use TypeDescriptor.GetProvider(MyClass/myObject).GetTypeDescriptor(MyClass/myObject) to get the type or object's baseline ICustomTypeDescriptor
Construct his PropertyOverridingTypeDescriptor with this baseline descriptor
Iterate through MyClass/myObject's properties' definitions with TypeDescriptor.GetProperties(MyClass/myObject). Use TypeDescriptor.CreateProperty to create a new property definition based on the current property's definition, that adds the custom attribute EditorAttribute (or in your case SubmitAttribute), and use the PropertyOverridingTypeDescriptor constructed in 3. to use the new property definition.
Construct his TypeDescriptorOverridingProvider with the PropertyOverridingTypeDescriptor constructed in 3.
Apply the new property definitions to MyClass/myObject with TypeDescriptor.AddProvider
One more way to add attributes to a dynamically generated class is to add one more command line app to your code generation pipeline.
Here's an example how to rewrite C# code file with Microsoft.CodeAnalysis.CSharp library:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System;
using System.IO;
namespace SB.Common.ContractGenerator
{
class SubmitAttributeAdder : CSharpSyntaxRewriter
{
public override SyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax node)=>
node.WithAttributeLists(
node.AttributeLists.Count == 0
? node.AttributeLists.Add(SyntaxFactory.AttributeList()
.AddAttributes(SyntaxFactory.Attribute(
SyntaxFactory.ParseName("Submit")))
// Add some whitespace to keep the code neat.
.WithLeadingTrivia(node.GetLeadingTrivia())
.WithTrailingTrivia(SyntaxFactory.Whitespace(Environment.NewLine)))
: node.AttributeLists);
}
class Program
{
static void Main(string[] args)
{
// The file name to be injected as a command line parameter
var tree = CSharpSyntaxTree.ParseText(
SourceText.From(File.ReadAllText("Test.cs")));
File.WriteAllText("Test.cs",
new SubmitAttributeAdder().Visit(tree.GetRoot()).ToString());
}
}
}
First, the input C# code file is parsed as the syntax tree, and then the SubmitAttributeAdder class skims through all declared classes and their properties to amend an attribute list for each of them.
After that the modified syntax tree is saved back to the same file.
Here the app only adds the Submit attribute in case the attribute list of a property is empty, but one can easily make the logic more sophisticated - e.g. check whether there are other attributes, add corresponding using <namespace> for the Submit attribute and so on.
An example Test.cs file before running the app:
namespace MyProject
{
class MyClass
{
public int num1 { get; set; }
public int num2 { get; set; }
public string str1 { get; set; }
public string str2 { get; set; }
}
}
...and after:
namespace MyProject
{
class MyClass
{
[Submit]
public int num1 { get; set; }
[Submit]
public int num2 { get; set; }
[Submit]
public string str1 { get; set; }
[Submit]
public string str2 { get; set; }
}
}
I wrote myself a library to help with generic database lookups. When I use it I get a class that has all properties blank. However, the base class is properly filled. The product variable is properly filled. How I can make the code fill the derived class (TModel entity)? When I set breakpoint in the dataservice Create method (comment inside code) these are the results in Locals/Autos window:
public class GenericLookupModelDataService<TModel, TViewModel> : object, IDisposable
where TModel : GenericLookupModel, new()
where TViewModel : GenericLookupViewModel, new()
public virtual void Create(TViewModel product, string username = "SYSTEM")
{
TModel entity = new TModel
{
is_active = product.Active,
value = product.Name,
created_on = product.CreatedOn,
created_by = product.CreatedBy,
modified_on = product.ModifiedOn,
modified_by = product.ModifiedBy
};
if (string.IsNullOrEmpty(entity.created_by)) //breakpoint here
entity.SetCreated(username);
if (string.IsNullOrEmpty(entity.modified_by))
entity.SetModified(username);
_db.Set<TModel>().Add(entity);
_db.SaveChanges();
}
TViewModel based on GenericLookupViewModel class:
public abstract class GenericLookupViewModel
{
[Key]
public int ID { get; set; }
[Required]
[StringLength(300)]
public string Name { get; set; }
[Required]
public bool Active { get; set; }
[StringLength(50)]
[DisplayName("Record last modified by")]
public string ModifiedBy { get; set; }
[DisplayName("Record last modified Date")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime ModifiedOn { get; set; }
[StringLength(50)]
[DisplayName("Record created by")]
public string CreatedBy { get; set; }
[DisplayName("Record creation Date")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime CreatedOn { get; set; }
}
TModel based on GenericLookupModel class:
public abstract class GenericLookupModel : IActive, ICreated, IModified, IIdentity, IStringValue
{
public bool is_active { get; set; }
public string value { get; set; }
public DateTime created_on { get; set; }
public string created_by { get; set; }
public DateTime modified_on { get; set; }
public string modified_by { get; set; }
public int id {get;set;}
public void SetCreated(string creator = "SYSTEM")
{
created_by = creator;
created_on = DateTime.Now;
}
public void SetModified(string modifier = "SYSTEM")
{
modified_by = modifier;
modified_on = DateTime.Now;
}
public void ToggleActive()
{
is_active = !is_active;
}
}
Controller and Action:
public class PrimaryFocusController : GenericLookupViewModelController<PrimaryFocusViewModel,tblkp_PrimaryFocus>
{
public override ActionResult Create(PrimaryFocusViewModel lookup)
{
SetBrowsingUser(AppUser.Login);
return base.Create(lookup);
}
}
When I compile the library I get warning messages which might have to do something on this:
warning CS0108: 'DataLayer.tblkp_PrimaryFocus.id' hides inherited member 'MyLib.Model.GenericLookupModel.id'. Use the new keyword if hiding was intended.
warning CS0108: 'DataLayer.tblkp_PrimaryFocus.value' hides inherited member 'MyLib.Model.GenericLookupModel.value'. Use the new keyword if hiding was intended.
warning CS0108: 'DataLayer.tblkp_PrimaryFocus.is_active' hides inherited member 'MyLib.Model.GenericLookupModel.is_active'. Use the new keyword if hiding was intended.
warning CS0108: 'DataLayer.tblkp_PrimaryFocus.created_on' hides inherited member 'MyLib.Model.GenericLookupModel.created_on'. Use the new keyword if hiding was intended.
warning CS0108: 'DataLayer.tblkp_PrimaryFocus.created_by' hides inherited member 'MyLib.Model.GenericLookupModel.created_by'. Use the new keyword if hiding was intended.
warning CS0108: 'DataLayer.tblkp_PrimaryFocus.modified_on' hides inherited member 'MyLib.Model.GenericLookupModel.modified_on'. Use the new keyword if hiding was intended.
warning CS0108: 'DataLayer.tblkp_PrimaryFocus.modified_by' hides inherited member 'MyLib.Model.GenericLookupModel.modified_by'. Use the new keyword if hiding was intended.
The DataLayer.tblkp_PrimaryFocus is a class generated from EF using DB First approach.
UPDATE: User #code4life brought a good point - to have all those properties of the child classes marked as virtual (tblkp_PrimaryFocus), but that would mean that I would need to mark all of them each time model is regenerated from EF Diagram - that's what I'm trying to avoid - modifying the EF-generated classes.
I know it is late to the game, but I stumbled in here struggling with some of the same issues. The first point for anyone to get into the code is to follow msdn. The main take-away to get started is add debug="true to the top line in the .tt file, e.g., <## template language="C#" debug="false" hostspecific="true"#> becomes <## template language="C#" debug="true" hostspecific="true"#>. Now you can debug the code by right clicking the .tt files.
The place I think the questionnaire was referring is to simpleProperties in the .tt file. Here you can add override or other keywords if need be :)
You can modify the T4 templates (.tt files) to mark them as overriding or new as appropriate. The properties on the base type would have to be marked virtual of course.
I believe overriding would work as the behavior you observe seems to be due to your Create method not being able to access the inherited properties (it only has the definitions of the base, and they're not virtual). That said, without getting too deep into your design I imagine this is not exactly what you want.
What you'd really want is to prevent the code generator from emitting the properties on the derived class to begin with so that all callers use the base definitions. I suggest you first look into the T4 templates again to see if you can't add rules that match against properties of your base type. Alternatively, consider custom attributes to match them.
It feels like there's a general way to solve this issue, so consider formulating a report for the EF team if you can confirm (I haven't thought about it very much).
Expect recommendations to switch to a Code-First approach however. Frankly, I'm tempted to recommend it to you myself.
I am very new in dynamodb. I am following http://www.rkconsulting.com/blog/persistence-model-framework-with-aws-dynamodb
step by step tutorial for connecting and CRUD operation in dynamodb and it`s works fine.
In that tutorial they using attribute mapping for map class properties
[DynamoDBTable("Dinosaur")]
public class Dinosaur
{
[DynamoDBHashKey]
public string Id { get; set; }
[DynamoDBProperty(AttributeName = "Name")]
public string Name { get; set; }
[DynamoDBProperty(AttributeName = "HeightMetres")]
public double HeightMetres { get; set; }
[DynamoDBProperty(AttributeName = "WeightKG")]
public double WeightKg { get; set; }
[DynamoDBProperty(AttributeName = "Age")]
public int Age { get; set; }
[DynamoDBProperty(AttributeName = "Characteristics")]
public List<string> Characteristics { get; set; }
[DynamoDBProperty(AttributeName = "Photo", Converter = typeof(ImageConverter))]
public Image Photo { get; set; }
[DynamoDBIgnore]
public int IgnoreMe { get; set; }
}
My question is there any way to map class properties without using attribute ?
like as mongoDb
public class Employee
{
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
}
we can write this in this way in a separate class
BsonClassMap.RegisterClassMap<Employee>(cm => {
cm.AutoMap();
cm.IdMemberMap.SetRepresentation(BsonType.ObjectId);
});
Is it possible in dynamodb ?
In the latest version of the .NET SDK you don't have to put in the attribute tags, it will see all read/write properties and upload the attributes as the same name. You would only have to use the [DynamoDBProperty(...)] if you want the attribute name in DynamoDB to be something other than the .NET object name.
So in your case you could simply remove that attribute for all properties except photo (which needs the converter, you could remove the AttributeName part of it) and WeightKg (because the capitalization is different) and you would get the same result.
I see this is a little bit older question now, so it may not have been that way in older versions (not sure) but I'm using 3.3.0.0 of the SDK and it does work that way. You have probably moved on but answering for others that may come upon this thread as I did...
There is no way, the default "strongly typed" client relies on attributes.
If you have time to do the plumbing yourself - there is nothing stopping your from doing your own implementation of the POC to Dynamo mapping though. Amazon client api (AWSSDK.DynamoDBv2) exposes the raw class AmazonDynamoDBClient which handles all the API calls and the DynamoDBConext is just implementation of IDynamoDBContext interface - which exposes all the "strongly typed" operations. So you can make your own implementation and take different mapping approach in it.
Also you can make a feature request for this:
https://github.com/aws/aws-sdk-net/issues
I have an interface ITranslateStuff and a static class and method with a generic parameter that is constrained (where class, ITranslateStuff, new()).
string translation = Translator.TranslateStuff<ITranslateStuff>();
Depending on which implementation of ITranslateStuff that I pass the method returns a different string.
I have ViewModels with a lot of different properties wich returns implementations of ITranslateStuff, for example:
public class ExampleViewModel
{
public string OtherStuff {get; set; }
public string TranslateStuffExample1 Translations { get; set; }
public ExampleViewModel2 SubModel {get; set; }
}
public class ExampleViewModel2
{
public string MoreStuff { get; set; }
public string TranslateStuffExample2 Translations { get; set; }
}
where both DoStuffExample1 and DoStuffExample2 implements ITranslateStuff.
I'm currently populating all theese properties with code like this:
model.Translations = Translator.TranslateStuff<TranslateStuffExample1>();
model.SubModel.Translations = Translator.TranslateStuff<TranslateStuffExample2>();
In the project we use StructureMap. I want to avoid setting all the properties on my view model manually with the same static method call. I have an ActionFilter where I set common properties on my view model, and was thinking I want to do this in an action filter as well.
I've tried finding something in StructureMap that can do this for me.
How can I solve this?
You will want to use 'setter injection'.
http://docs.structuremap.net/ConstructorAndSetterInjection.htm#section4