I'm experimenting with the Neo4jClient in C# and am stuck at the following error:
The best overloaded method match for 'Neo4jClient.IGraphClient.CreateRelationship(Neo4jClient.NodeReference, GraphDB.PrecedesRelationshipo)' has some invalid arguments.
This error is for the line with the following code:
client.CreateRelationship<Process,PrecedesRelationship>(prevProcess, new PrecedesRelationship(currProcess, new PrecedesData(product, isOptional)));
Here, prevProcess and currProcess are both of type Neo4jClient.NodeReference. Actually, I generate the nodes and store their NodeReference values in a dictionary, so that I can easily look them up. The nodes are created just fine.
Below are my classes:
public class Process
{
public string Name { get; set; }
}
,
public class PrecedesData
{
public string Name { get; set; }
public bool IsOptional { get; set; }
public PrecedesData()
{ }
public PrecedesData(string name)
{
this.Name = name;
this.IsOptional = false;
}
public PrecedesData(string name, bool isOptional)
{
this.Name = name;
this.IsOptional = IsOptional;
}
}
and
public class PrecedesRelationship : Relationship<PrecedesData>, IRelationshipAllowingSourceNode<Process>,
IRelationshipAllowingTargetNode<Process>
{
public static readonly string TypeKey = "PRECEDES";
public PrecedesRelationship(NodeReference targetNode, PrecedesData data)
: base(targetNode, data)
{ }
public override string RelationshipTypeKey
{
get { return TypeKey; }
}
}
When I leave out the types in CreateRelationship I get the error that the compiler cannot infer the types.
I looked at the examples on the Neo4jClient Wiki and I thought I got it right but I seem to be mistaken.
What am I missing here?
You should be using Cypher, as the REST API is increasingly legacy. Really, anything non-Cypher is becoming legacy.
client.Cypher
.Start(new { prevProcess, currProcess })
.CreateUnique("prevProcess-[:PRECEDES {precedes}]->currProcess")
.WithParams(new { precedes = new PrecedesData(product, isOptional) })
.ExecuteWithoutResults();
Then, you don't need any relationship classes either.
Also, if you remove the excess constructors, you can shorten the entire code sample down to just this:
public class PrecedesData
{
public string Name { get; set; }
public bool IsOptional { get; set; }
}
client.Cypher
.Start(new { prevProcess, currProcess })
.CreateUnique("prevProcess-[:PRECEDES {precedes}]->currProcess")
.WithParams(new { precedes = new PrecedesData { Name = product, IsOptional = isOptional } })
.ExecuteWithoutResults();
Related
I have a problem to get the right OpenApi definition aftr update from 5.0.0 to 5.4.1
We had custom Polymorphism filter with 5.0.0 version, but they does not work correct with latest one. So I removed them and started to use GeneratePolymorphicSchemas(). It does what I need for our polymorphic models but not just for them. We have also some other abstract and concrete classes, where we don't need type discriminator. I tried different configurations but without any success. Either the generated definition is wrong or I get error on swagger UI or a server 503 error.
Link to the sample project Sample project
Here are my polimorhic models
namespace SwashbuckleTest.Models
{
public interface ITypeDiscriminator
{
string TypeDiscriminator { get; }
}
public abstract class SurveyStep : ITypeDiscriminator
{
public virtual string Id { get; set; }
public string TypeDiscriminator => GetType().Name;
}
public abstract class SurveyStepResult : ITypeDiscriminator
{
public string Id { get; set; }
public string TypeDiscriminator => GetType().Name;
}
public class BoolStep : SurveyStep
{
private string _id;
public BoolStep()
{
ResultObject = new BoolStepResult();
}
public override string Id
{
get => _id;
set
{
_id = value;
ResultObject.Id = value;
}
}
public string Question { get; set; }
public BoolStepResult ResultObject { get; }
}
public class BoolStepResult : SurveyStepResult
{
public bool Value { get; set; }
}
}
Here other models
namespace SwashbuckleTest.Models
{
public abstract class SomeBaseModel
{
public string BaseValue { get; set; }
}
public class SomeConcreteModel : SomeBaseModel
{
public int ConcreteValue { get; set; }
}
}
and configurations I have tried
options.UseAllOfToExtendReferenceSchemas();
options.GeneratePolymorphicSchemas(t =>
{
var types = t.Is<SurveyStep>() ? new List<Type>() {typeof(BoolStep)}
: t.Is<SurveyStepResult>() ? new List<Type>() {typeof(BoolStepResult)}
: null;
return types;
} , t => t.Is<ITypeDiscriminator>() ? nameof(ITypeDiscriminator.TypeDiscriminator).ToCamelCase() : null);
// or
options.GeneratePolymorphicSchemas(discriminatorSelector: t => t.Is<ITypeDiscriminator>() ? nameof(ITypeDiscriminator.TypeDiscriminator).ToCamelCase() : null);
I found the problem by my self.
The Is<> extension method does not filter abstract classes so we got here endless recursion.
It helped us to generate swagger.json, but we got other problems, that are little bit deeper.
I'm writing a tool which accesses a word document to prefill it with data. The document has a subset of custom document properties, each identified by a name, whose values are used to update fields in the document.
My ViewModel should both be able to initiate/update its instances from data of those document properties, aswell as write its values back and update the fields of the document.
Something like this:
class PersonVM : INotifyPropertyChanged
{
// properties
string Name { get; set; }
string PhoneNumber { get; set; }
// methods to get data or save data of this properties to or from the word document
void saveMyPropertyValuesToWord()
{
// …
}
void updateMyPropertiesFromWord()
{
// …
}
}
class ProjectVM : INotifyPropertyChanged
{
int ProjectNumber { get; set; }
PersonVM Manager { get; set; }
PersonVM Mechanic1 { get; set; }
PersonVM Mechanic2 { get; set; }
void saveMyPropertyValuesToWord()
{
Manager.saveMyPropertyValuesToWord();
Mechanic1.saveMyPropertyValuesToWord();
Mechanic2.saveMyPropertyValuesToWord();
// handle ProjectNumber etc.
}
void updateMyPropertiesFromWord()
{
Manager.updateMyPropertiesFromWord();
Mechanic1.updateMyPropertiesFromWord();
Mechanic2.updateMyPropertiesFromWord();
// handle ProjectNumber etc.
}
class CompanyVM : INotifyPropertyChanged
{
string Name { get; set; }
PersonVM Owner { get; set; }
ProjectVM Project1 { get; set; }
ProjectVM Project2 { get; set; }
// …
}
// …
}
Right now I have a class with static string properties for each document property that might be present in a word document from which I would like to load the data accordingly:
class WordUtils
{
// Company
static string CompanyName = "dp_CompanyName";
// Company.Owner
static string CompanyOwnerName = "dp_CompanyOwnerName";
static string CompanyOwnerPhone = "dp_CompanyOwnerPhone";
// Company.Project1
static string CompanyProject1Number = "dp_CompanyProject1Number";
// Company.Project1.Manager
static string CompanyProject1ManagerName = "dp_CompanyProject1ManagerName";
static string CompanyProject1ManagerPhone = "dp_CompanyProject1ManagerPhone";
// Company.Project1.Mechanic1
// … etc
}
Now back to implementing those PersonVM.saveMyPropertyValuesToWord() - I thought of something like this:
void saveMyPropertyValuesToWord()
{
Name = MyApp.MyWordDocument.GetCustomProperty(WordUtils.OwnerName);
}
but here I need to know on class Level exactly what instance of it this is called from (i.e. what PersonVM am I, Company.Owner or Project1.Manager or ?) in order to decide which WordUtils.Name I need to provide.
I'm not sure how this should be done, maybe make PersonVM abstract and make a new class for each role (which would again only have one instance of itself, not very pretty in my eyes)? I have also taken a short look at Attributes and expect those might be helpfull in this scenario. Maybe I am missing something obvious, but extensive search for a robust way to tackle this problem have been fruitless so far.
How about something like this:
class Property
{
public string Key { get; }
public string Value { get; set; }
public Property(string key) => Key = key;
}
interface IPropertyTree
{
IEnumerable<IPropertyTree> ChildNodes { get; }
IEnumerable<Property> Properties { get; }
}
class PersonVM : IPropertyTree
{
private readonly string prefix;
public PersonVM(string prefix)
{
Name = new Property(prefix + "Name" );
PhoneNumber = new Property(prefix + "PhoneNumber");
}
public Property Name { get; }
public Property PhoneNumber { get; }
public IEnumerable<IPropertyTree> ChildNodes => Enumerable.Empty<IPropertyTree>();
public IEnumerable<Property> Properties => new[] {Name, PhoneNumber};
}
static class PropertyTreeExtensions
{
public static void Update(this IPropertyTree self)
{
foreach (var property in self.Flatten().SelectMany(tree => tree.Properties))
{
property.Value = MyApp.MyWordDocument.GetCustomProperty(property.Key);
}
}
public static IEnumerable<IPropertyTree> Flatten(this IPropertyTree self)
{
var stack = new Stack<IPropertyTree>();
stack.Push(self);
while (stack.Count > 0)
{
var current = stack.Pop();
yield return current;
foreach (var child in current.ChildNodes)
{
stack.Push(child);
}
}
}
}
This should allow each property to have a unique key, and keep the key and property value tightly coupled. It should also allow you to move the save/update logic to a centralized place.
Of course you can implement a concrete class of IPerson for each type and hard code the individual implementations.
Since you know the person type the moment you are creating an instance of PersonVMM, you could add an attribute PersonTypeId and set it from the constructor,
void SomeMethod()
{
var personVm = new PersonVM(WordUtils.OwnerName);
}
class PersonVM : INotifyPropertyChanged
{
// properties
string PersonTypeId { get; set; }
string Name { get; set; }
string PhoneNumber { get; set; }
public PersonVM()
{}
public PersonVM(string personTypeId)
{
PersonTypeId = personTypeId;
}
// methods to get data or save data of this properties to or from the word document
void saveMyPropertyValuesToWord()
{
Name = MyApp.MyWordDocument.GetCustomProperty(PersonTypeId);
}
}
I had a question with regards to custom authorization for AWS API Gateway using a lambda coded in C#. In the documentation for AWS Lambdas, the function signature is as follows:
returnType handler-name(inputType input, ILambdaContext context) {
...
}
The inputType and returnType need to be specified for the function handler. For custom authorization in API Gateway, what should the inputType and returnTypes be? Thanks in advance.
You can opt for a strongly-typed approach without inventing custom classes that need to follow the required schema.
Use Nuget package:
Amazon.Lambda.APIGatewayEvents
Input schema:
https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-lambda-authorizer-input.html
Output schema:
https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-lambda-authorizer-output.html
Your function prototype can then resemble:
using Amazon.Lambda.APIGatewayEvents;
using Amazon.Lambda.Core;
public class Function
{
public APIGatewayCustomAuthorizerResponse FunctionHandler(APIGatewayCustomAuthorizerRequest input, ILambdaContext context)
{
bool ok = false;
// authorization logic here...
if(input.AuthorizationToken == "up-down-left-right-a-b-select-start")
{
ok = true;
}
return new APIGatewayCustomAuthorizerResponse
{
PrincipalID = "***",//principal info here...
UsageIdentifierKey = "***",//usage identifier here (optional)
PolicyDocument = new APIGatewayCustomAuthorizerPolicy
{
Version = "2012-10-17",
Statement = new List<APIGatewayCustomAuthorizerPolicy.IAMPolicyStatement>() {
new APIGatewayCustomAuthorizerPolicy.IAMPolicyStatement
{
Action = new HashSet<string>(){"execute-api:Invoke"},
Effect = ok ? "Allow" : "Deny",
Resource = new HashSet<string>(){ "***" } // resource arn here
}
},
}
};
}
}
I thought I would elaborate this a bit. This uses part of what was done here as well as tried to make it like the example they give us here.
http://docs.aws.amazon.com/apigateway/latest/developerguide/use-custom-authorizer.html
I am not sure if it needs to be async or not? I didn't and this seemed to work pretty well for a basic start.
public class Authorize
{
public Authorize() { }
public AuthPolicy AuthorizeHandler(TokenAuthorizerContext request, ILambdaContext context)
{
var token = request.AuthorizationToken;
switch (token.ToLower())
{
case "allow":
return generatePolicy("user", "Allow", request.MethodArn);
}
return null;
}
private AuthPolicy generatePolicy(string principalId, string effect, string resource)
{
AuthPolicy authResponse = new AuthPolicy();
authResponse.policyDocument = new PolicyDocument();
authResponse.policyDocument.Version = "2012-10-17";// default version
authResponse.policyDocument.Statement = new Statement[1];
Statement statementOne = new Statement();
statementOne.Action = "execute-api:Invoke"; // default action
statementOne.Effect = effect;
statementOne.Resource = resource;
authResponse.policyDocument.Statement[0] = statementOne;
return authResponse;
}
}
public class TokenAuthorizerContext
{
public string Type { get; set; }
public string AuthorizationToken { get; set; }
public string MethodArn { get; set; }
}
public class AuthPolicy
{
public PolicyDocument policyDocument { get; set; }
public string principalId { get; set; }
}
public class PolicyDocument
{
public string Version { get; set; }
public Statement[] Statement { get; set; }
}
public class Statement
{
public string Action { get; set; }
public string Effect { get; set; }
public string Resource { get; set; }
}
I wanted to post the solution that I used that worked for me. Thanks to Josh Maag for pointing me in the right direction. Basically, I created a few simple classes:
public class TokenAuthorizerContext
{
public string Type { get; set; }
public string AuthorizationToken { get; set; }
public string MethodArn { get; set; }
}
public class AuthPolicy
{
public PolicyDocument policyDocument { get; set; }
public string principalId { get; set; }
}
public class PolicyDocument
{
public string Version { get; set; }
public Statement[] Statement { get; set; }
}
public class Statement
{
public string Action { get; set; }
public string Effect { get; set; }
public string Resource { get; set; }
}
```
With the above classes created, the signature to my handler is:
public async Task<AuthPolicy> FunctionHandler(TokenAuthorizerContext request, ILambdaContext context)
You should really take a look at the following link and try to follow it through. The full tutorial is written using Python, so if you're unfamiliar with it, just do your best to follow along and read the full walk-through, but this link will explain the C# portion:
http://docs.aws.amazon.com/lambda/latest/dg/get-started-step5-optional.html
Essentially, the string:
returnType handler-name(inputType input, ILambdaContext context) {
Would be something like this (copied from the AWS page):
public string MyHandler(int count, ILambdaContext context) { ... }
public is added as a scope modifier, the returnType the developer has chosen is string the handler-name is MyHandler and the inputType is int
So I'm not sure if you can do this or not, but I would like to avoid using SQL strings if I can. What I would like to do with Linq/DbContexts is the following that can be done easily with SQL:
string sql = "UPDATE " + tableName + " SET Status=0 WHERE Id=" + formId.ToString();
I can easily put this into a loop where tableName and formId are given dynamically and execute no problem.
I have multiple DbContexts, so I don't know of any way to do something like:
var db = new *dynamicallyChosenContext*()
var query = from p in db.*dynamicallyChosenAlso*
where p.Id == formId
select p;
foreach (var result in query)
{
result.Status = 0;
}
db.SaveChanges()
Thanks for the help!
Here is a piece of working code that can update different tables at runtime from different contexts without using reflection.
namespace DemoContexts
{
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
public interface IThing
{
int Id { get; set; }
int Status { get; set; }
}
public class FirstPersonThing : IThing
{
[System.ComponentModel.DataAnnotations.Key]
public int Id { get; set; }
public int Status { get; set; }
public string Foo { get; set; }
}
public class SecondPersonThing : IThing
{
[System.ComponentModel.DataAnnotations.Key]
public int Id { get; set; }
public int Status { get; set; }
public string Bar { get; set; }
}
public class FirstContext : DbContext
{
public FirstContext() : base("FirstContext") { }
public DbSet<FirstPersonThing> MyThings { get; set; }
public DbSet<SecondPersonThing> YourThings { get; set; }
}
public class SecondContext : DbContext
{
public SecondContext() : base("SecondContext") { }
public DbSet<FirstPersonThing> MyThings { get; set; }
public DbSet<SecondPersonThing> YourThings { get; set; }
}
class Program
{
static void Main(string[] args)
{
int contextType = 1;
int thingType = 1;
DbContext db = RunTimeCreatedContext(contextType);
IQueryable<IThing> collection = RunTimeCreatedCollection(db, thingType);
UpdateRuntimeDeterminedThings(db, collection, 1);
Console.ReadLine();
}
public static void UpdateRuntimeDeterminedThings(DbContext db,
IQueryable<IThing> collection,
int formId)
{
var querySet = collection.Where(p => p.Id == formId).ToList();
foreach (var result in querySet)
{
result.Status = 0;
}
db.SaveChanges();
}
static DbContext RunTimeCreatedContext(int contextType)
{
if (contextType == 0)
{
return new FirstContext();
}
else
{
return new SecondContext();
}
}
static IQueryable<IThing> RunTimeCreatedCollection(DbContext db, int thingType)
{
if (thingType == 0)
{
return db.Set(typeof(FirstPersonThing)) as IQueryable<IThing>;
}
else
{
return db.Set<SecondPersonThing>();
}
}
}
}
The first thing to note is that all this is statically typed so to perform a generic query against different types of objects these objects must have common property signatures and this conceptually is expressed in the IThing interface.
A second thing to note is how the IQueryable is generated. It is generated by the DbContext.Set Method (Type) in the first instance (for the FirstPersonThings), it is generated by the DbContext.Set<TEntity> Method in the second instance. The first uses a type determined at runtime and requires a cast (but could be useful to use passing types at runtime), the second uses generics and the types are determined at compile time. There are obviously a number of other ways that this function could work.
Finally the method UpdateRuntimeDeterminedThings works because it uses properties and methods that are shared across the types (either with base types/inheritance or by the implementation of interfaces).
None of this is actually dynamic programming (which is possible using the dynamic type) and I have used the term runtime determined rather than dynamic to describe how this works.
There is a technique in functional programming, called Currying, where you could pass as much parameters as you want, so you are able to access them.
Here is an exemple: http://blogs.msdn.com/b/sriram/archive/2005/08/07/448722.aspx
P.S: You could use a currying function to iterate through yours DBContexts.
I think you have to use reflection if you want to use 'code' and not sql strings. That's just how C# works... This is how you could do it:
using System;
using System.Data.Entity;
using System.Linq;
public class TestContext : DbContext
{
public DbSet<Box> Boxes { get; set; }
public DbSet<Thing> Things { get; set; }
}
public abstract class Base
{
public int Id { get; set; }
}
public class Box : Base
{
public string Title { get; set; }
}
public class Thing : Base
{
public string Name { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
var db = new TestContext();
DoIt(GetPropValue(db, "Boxes") as IQueryable<Base>);
DoIt(GetPropValue(db, "Things") as IQueryable<Base>);
}
private static void DoIt(IQueryable<Base> b)
{
Console.WriteLine(
b.Single(t => t.Id == 1).Id);
}
private static object GetPropValue(object src, string propName)
{
return src.GetType().GetProperty(propName).GetValue(src, null);
}
}
Obviously you can then put the strings in a list etc, whatever you need.
I have two C# classes that have many of the same properties (by name and type). I want to be able to copy all non-null values from an instance of Defect into an instance of DefectViewModel. I was hoping to do it with reflection, using GetType().GetProperties(). I tried the following:
var defect = new Defect();
var defectViewModel = new DefectViewModel();
PropertyInfo[] defectProperties = defect.GetType().GetProperties();
IEnumerable<string> viewModelPropertyNames =
defectViewModel.GetType().GetProperties().Select(property => property.Name);
IEnumerable<PropertyInfo> propertiesToCopy =
defectProperties.Where(defectProperty =>
viewModelPropertyNames.Contains(defectProperty.Name)
);
foreach (PropertyInfo defectProperty in propertiesToCopy)
{
var defectValue = defectProperty.GetValue(defect, null) as string;
if (null == defectValue)
{
continue;
}
// "System.Reflection.TargetException: Object does not match target type":
defectProperty.SetValue(viewModel, defectValue, null);
}
What would be the best way to do this? Should I maintain separate lists of Defect properties and DefectViewModel properties so that I can do viewModelProperty.SetValue(viewModel, defectValue, null)?
Edit: thanks to both Jordão's and Dave's answers, I chose AutoMapper. DefectViewModel is in a WPF application, so I added the following App constructor:
public App()
{
Mapper.CreateMap<Defect, DefectViewModel>()
.ForMember("PropertyOnlyInViewModel", options => options.Ignore())
.ForMember("AnotherPropertyOnlyInViewModel", options => options.Ignore())
.ForAllMembers(memberConfigExpr =>
memberConfigExpr.Condition(resContext =>
resContext.SourceType.Equals(typeof(string)) &&
!resContext.IsSourceValueNull
)
);
}
Then, instead of all that PropertyInfo business, I just have the following line:
var defect = new Defect();
var defectViewModel = new DefectViewModel();
Mapper.Map<Defect, DefectViewModel>(defect, defectViewModel);
Take a look at AutoMapper.
There are frameworks for this, the one I know of is Automapper:
http://automapper.codeplex.com/
http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/01/22/automapper-the-object-object-mapper.aspx
Replace your erroneous line with this:
PropertyInfo targetProperty = defectViewModel.GetType().GetProperty(defectProperty.Name);
targetProperty.SetValue(viewModel, defectValue, null);
Your posted code is attempting to set a Defect-tied property on a DefectViewModel object.
In terms of organizing the code, if you don't want an external library like AutoMapper, you can use a mixin-like scheme to separate the code out like this:
class Program {
static void Main(string[] args) {
var d = new Defect() { Category = "bug", Status = "open" };
var m = new DefectViewModel();
m.CopyPropertiesFrom(d);
Console.WriteLine("{0}, {1}", m.Category, m.Status);
}
}
// compositions
class Defect : MPropertyGettable {
public string Category { get; set; }
public string Status { get; set; }
// ...
}
class DefectViewModel : MPropertySettable {
public string Category { get; set; }
public string Status { get; set; }
// ...
}
// quasi-mixins
public interface MPropertyEnumerable { }
public static class PropertyEnumerable {
public static IEnumerable<string> GetProperties(this MPropertyEnumerable self) {
return self.GetType().GetProperties().Select(property => property.Name);
}
}
public interface MPropertyGettable : MPropertyEnumerable { }
public static class PropertyGettable {
public static object GetValue(this MPropertyGettable self, string name) {
return self.GetType().GetProperty(name).GetValue(self, null);
}
}
public interface MPropertySettable : MPropertyEnumerable { }
public static class PropertySettable {
public static void SetValue<T>(this MPropertySettable self, string name, T value) {
self.GetType().GetProperty(name).SetValue(self, value, null);
}
public static void CopyPropertiesFrom(this MPropertySettable self, MPropertyGettable other) {
self.GetProperties().Intersect(other.GetProperties()).ToList().ForEach(
property => self.SetValue(property, other.GetValue(property)));
}
}
This way, all the code to achieve the property-copying is separate from the classes that use it. You just need to reference the mixins in their interface list.
Note that this is not as robust or flexible as AutoMapper, because you might want to copy properties with different names or just some sub-set of the properties. Or it might downright fail if the properties don't provide the necessary getters or setters or their types differ. But, it still might be enough for your purposes.
This is cheap and easy. It makes use of System.Web.Script.Serialization and some extention methods for ease of use:
public static class JSONExts
{
public static string ToJSON(this object o)
{
var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
return oSerializer.Serialize(o);
}
public static List<T> FromJSONToListOf<T>(this string jsonString)
{
var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
return oSerializer.Deserialize<List<T>>(jsonString);
}
public static T FromJSONTo<T>(this string jsonString)
{
var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
return oSerializer.Deserialize<T>(jsonString);
}
public static T1 ConvertViaJSON<T1>(this object o)
{
return o.ToJSON().FromJSONTo<T1>();
}
}
Here's some similiar but different classes:
public class Member
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsCitizen { get; set; }
public DateTime? Birthday { get; set; }
public string PetName { get; set; }
public int PetAge { get; set; }
public bool IsUgly { get; set; }
}
public class MemberV2
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsCitizen { get; set; }
public DateTime? Birthday { get; set; }
public string ChildName { get; set; }
public int ChildAge { get; set; }
public bool IsCute { get; set; }
}
And here's the methods in action:
var memberClass1Obj = new Member {
Name = "Steve Smith",
Age = 25,
IsCitizen = true,
Birthday = DateTime.Now.AddYears(-30),
PetName = "Rosco",
PetAge = 4,
IsUgly = true,
};
string br = "<br /><br />";
Response.Write(memberClass1Obj.ToJSON() + br); // just to show the JSON
var memberClass2Obj = memberClass1Obj.ConvertViaJSON<MemberV2>();
Response.Write(memberClass2Obj.ToJSON()); // valid fields are filled
For one thing I would not place that code (somewhere) external but in the constructor of the ViewModel:
class DefectViewModel
{
public DefectViewModel(Defect source) { ... }
}
And if this is the only class (or one of a few) I would not automate it further but write out the property assignments. Automating it looks nice but there may be more exceptions and special cases than you expect.
Any chance you could have both classes implement an interface that defines the shared properties?