I like the approach of having property bag objects (DTOs) which define the interface to my server, but I don't like writing code like this:
void ModifyDataSomeWay(WibbleDTO wibbleDTO)
{
WibbleBOWithMethods wibbleBO = new WibbleBOWithMethods();
wibbleBO.Val1 = wibbleDTO.Val1;
wibbleBO.Val2 = wibbleDTO.Val2;
}
This copying code is quite laborious to write. If the copying code is unavoidable, then where do you put it? In the BO? In a factory? If it is possible to manually avoid writing the boiler plate code then how?
Thanks in advance.
That looks like a job for AutoMapper, or (simpler) just add some interfaces.
This needs more error handling, and you may need to modify it accommodate properties where the data types don't match, but this shows the essence of a simple solution.
public void CopyTo(object source, object destination)
{
var sourceProperties = source.GetType().GetProperties()
.Where(p => p.CanRead);
var destinationProperties = destination.GetType()
.GetProperties().Where(p => p.CanWrite);
foreach (var property in sourceProperties)
{
var targets = (from d in destinationProperties
where d.Name == property.Name
select d).ToList();
if (targets.Count == 0)
continue;
var activeProperty = targets[0];
object value = property.GetValue(source, null);
activeProperty.SetValue(destination, value, null);
}
}
Automapper (or similar tools) might be the way forward here. Another approach may be the factory pattern.
Simplest of all would be something like this:
class WibbleBO
{
public static WibbleBO FromData(WibbleDTO data)
{
return new WibbleBO
{
Val1 = data.Val1,
Val2 = data.Val2,
Val3 = ... // and so on..
};
}
}
Related
I am trying to set up the following code:
Controller
[HttpGet]
public ActionResult LoadReport(string actionName, string reportInput, string reportCriteria)
{
var type = Assembly.Load("Company.TaxCollection.Reports").GetTypes().First(t => t.Name == reportInput);
var typeCriteria = Assembly.Load("Company.TaxCollection.Reports").GetTypes().First(t => t.Name == reportInput + "Criteria");
var reportObject = Activator.CreateInstance(type);
var reportObjectCriteria = Activator.CreateInstance(typeCriteria);
IEnumerable<ReportCriteria> reportList = getReportCriteria(reportObject);
foreach (ReportCriteria r in reportList)
{
reportObjectCriteria t = (reportObjectCriteria)r;
}
return Json(Url.Action(actionName, "Reports", reportList.Where(x => x.CriteriaName == reportCriteria)));
}
I get the error reportObjectCriteria is a variable but is used like a type within the foreach loop.
I have also tried not using a variable and just using Activator.CreateInstance directly, but that didn't work either.
foreach (ReportCriteria r in reportList)
{
Activator.CreateInstance(typeCriteria) t =
(Activator.CreateInstance(typeCriteria)) r;
}
The purpose of these lines of code is to cast the ReportCriteria object to another type dynamically during runtime. The object type to cast to is decided by the reportInput parameter in the controller.
As long as you're not loading up a .dll dynamically at runtime, I would recommend using a library like Automapper. You can create multiple mappings like below and use conditional logic to determine which type to map to.
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<SourceType, DestType1>();
cfg.CreateMap<SourceType, DestType2>();
cfg.CreateMap<SourceType, DestType3>();
});
DestType1 dt1 = mapper.Map<DestType1>(sourceTypeInstance);
DestType2 dt2 = mapper.Map<DestType2>(sourceTypeInstance);
DestType3 dt3 = mapper.Map<DestType3>(sourceTypeInstance);
The library is very feature rich where you can configure mappings to a much deeper levels and lists/arrays etc.
I have a table called "Account." Account has 3 columns: id, acct_name, is_privileged.
When I write something like "account.", visual studio provides me with a list of attributes/methods I can use. Hence, I get the option of using account.id, account.acct_name, and account.is_privileged.
However, I would like to change a particular column's value dynamically, without typing in my column's name. I am getting the column's name dynamically as a string variable. Is it possible to achieve it? If so, how?
My code is as follows:
set_col_name = rowRule.Cells["setcolumnnameDataGridViewTextBoxColumn"].Value.ToString();
set_col_value = rowRule.Cells["setcolumnvalueDataGridViewTextBoxColumn"].Value.ToString();
foreach (DataGridViewRow rowAcc in dgvAccount.Rows)
{
if (isComparable(rowAcc.Cells[col_name].Value.ToString(), comp_operator, col_value))
{
account.id = (int)rowAcc.Cells["idDataGridViewTextBoxColumn2"].Value;
using (ae = new AccountEntities())
{
var temp = ae.Accounts.SingleOrDefault(a => a.id == account.id);
temp.is_privileged = set_col_value; //learn how to do this dynamically
ae.SaveChanges();
}
}
}
Where I do temp.is_privileged, I'd like to achieve something like, temp."set_col_name" = set_col_value;
Instead of specifying the column name directly as being "is_privileged" in this case, I'd like to pass a string to specify it.
Thank you.
If I understand your problem statement correctly, you want something like this to work:
Account temp = // with temp coming from a library such as EntityFramework
temp.SetValue(set_col_name, set_col_value);
this is quite easy to achieve with either pure reflection or Linq Expression Trees (which I opted for):
static class Ext
{
public static void Set<T, TProperty>(this T instance, string propertyName, TProperty value)
{
var instanceExpression = Expression.Parameter(typeof(T), "p");
var propertyGetterExpression = Expression.PropertyOrField(instanceExpression, propertyName);
//generate setter
var newValueExpression = Expression.Parameter(typeof(TProperty), "value");
var assignmentExpression = Expression.Assign(propertyGetterExpression, newValueExpression);
var lambdaExpression = Expression.Lambda<Action<T, TProperty>>(assignmentExpression, instanceExpression, newValueExpression);
var setter = lambdaExpression.Compile();// the generated lambda will look like so: (p, value) => p.{your_property_name} = value;
setter(instance, value);
}
}
one advantage of this method over pure reflection is that you can build the setter delegate once and call it multiple times at later stage (I will leave this with you to experiment)
with the above in place, hopefully you should be able to do something like this:
var set_col_name = "is_privileged";
var set_col_value = true;
using (ae = new AccountEntities())
{
var temp = ae.Accounts.SingleOrDefault(a => a.id == account.id);
temp.Set(set_col_name, set_col_value);
temp.Set("acct_name", "test");
ae.SaveChanges();
}
You need some reflection in this one. For example
public static void CopyValues<T>(T obj1, T obj2)
{
var type = typeof(T);
foreach (var prop in type.GetProperties())
{
prop.SetValue(obj1, prop.GetValue(obj2));
}
}
And use the above function like this:
var source = new Accounts(){is_privileged = false};
var destiny = new Accounts();
CopyValues(source, destiny);
It depends of what you are loking for, but the key is to use REFLECTION!
So we have a document database which stores our data as XML files. A new requirement is to create a graphQL interface to access the xmldocument.
I have implemented the graphQL interface with extensions:
https://github.com/graphql-dotnet/graphql-dotnet
https://github.com/graphql-dotnet/server
So far I can create the fields dynamically from the XML file:
public ReferenceType(IRepository repository)
{
var searchResult = repository.GetExampleForSchema();
foreach(var field in searchResult.Root.Elements())
{
if (!field.HasElements && field.Attribute(json + "Array") == null)
{
Field<StringGraphType>(field.Name.LocalName);
}
else
{
//TODO: Create child ObjectGraphTypes
}
}
}
But I can't find out how to return the result in my repository:
public dynamic Search()
{
var searchResult = new ExpandoObject() as IDictionary<string, object>;
//var searchResult = new { recordCount = 124345};
var xDocSearch = InternalSearch();
foreach (var field in xDocSearch.Root.Elements())
{
if (!field.HasElements && field.Attribute(json + "Array") == null)
{
var fieldName = field.Name.LocalName;
searchResult[fieldName] = field.Value;
}
else
{
//TODO: Create child objects
}
}
return searchResult;
}
The issue is, that GraphQL works with Reflection and uses this logic to read the field:
https://github.com/graphql-dotnet/graphql-dotnet/blob/master/src/GraphQL/Resolvers/NameFieldResolver.cs
The ExpandoObject does not have my example recordCount as a property. When I create an anonymous type (see the commented line) it works.
So my questions are:
Is there a possibility to create a type with dynamic properties which can be accessed by reflections? (Can't influence how the value is read)
Is there maybe another way in the GraphQL Extension to solve this issue? Can I configure how the value is read?
Looking forward to some hints and tips!
Thanks,
Michael
So after some research and trying around I could solve it by myself:
It is possible to provide a resolve method for each field:
if (!field.HasElements && field.Attribute(json + "Array") == null)
{
Field<StringGraphType>(field.Name.LocalName, **resolve: ResolveXml**);
}
and in this method you can do what you want:
public object ResolveXml(ResolveFieldContext<object> context)
{
var source = (IDictionary<string, object>)context.Source;
return source[context.FieldName];
}
This is now only the first working solution, of course it might make more sense to return in the repository directly the XDocument and then resolve it here...
Maybe it helps someone as well!
I'm using Newtonsoft.Json lib with C# and encountered a problem. I have a jArray as below:
[{"category": "computer","subcat": "desktop"},
{"category": "category","subcat": "laptop"},
{"category": "television","subcat": "LCD"}]
and I want it to be transformed as a jObject as below:
{"computer": ["desktop","laptop"],
"television": ["LCD"]}
Here want I need is a general method that can handle this type of transformation. Which means this method could also do the trick even if the jArray has a 3rd level property. e.g.
from
[{"typeA":"a","typeB":"a1","typeC":"a11"},{"typeA":"a","typeB":"a1","typeC":"a12"},
{"typeA":"a","typeB":"a2","typeC":"a21"}]
to
{"a":{"a1":["a11","a12"],"a2":["a21"]}}
Thanks a lot!
EXTREMELY quick and dirty - with vast room for improvement, but this will get it done:
Use like this:
var input = #"[{""type"":""a"",""typeB"":""a1"",""typeC"":""a11""},
{""type"":""a"",""typeB"":""a1"",""typeC"":""a12""},
{""type"":""a"",""typeB"":""a2"",""typeC"":""a21""}]";
var b = JsonConvert.DeserializeObject<List<tmp>>(input);
var result = doIt(b, "type", "typeB", "typeC");
With this implementation:
private string doIt(IEnumerable<tmp> objs, params string[] g)
{
var t = CollapseOrGroup(objs,g);
return JsonConvert.SerializeObject(t);
}
private dynamic CollapseOrGroup(IEnumerable<tmp> objs, IEnumerable<string> props)
{
var firstProp = props.FirstOrDefault();
if (firstProp == default(string))
return objs;
var p = Expression.Parameter(typeof(tmp));
var m = Expression.Property(p, firstProp);
var l = Expression.Lambda(m, p).Compile() as dynamic;
if (props.Count() == 1)
{
return Enumerable.Select(objs, l);
} else {
IEnumerable<IGrouping<dynamic, tmp>> g = Enumerable.GroupBy(objs, l);
return g.ToDictionary (o => o.Key, o => CollapseOrGroup(o, props.Skip(1)));
}
}
Prints:
{"a":{"a1":["a11","a12"],"a2":["a21"]}}
Search for Newtonsoft in NuGet and download JSON.Net for this to work
I have encountered the same problem using java.
Currently, the solution from my side is to traversal the initial ArrayJSON, then figure out the wanted format.
You can define Type1 as a model, List as the member variable, same operation with Type2 using Type3, this will help make your code easy to be organised.
this might be a simple fix but I can't seem to find anything about it. I am very new to C# if it's not obvious.
I'm passing a list of objects from my main method but I haven't been able to use the properties of the objects. I want to use a property called "Asset" This is my code:
private void GetDueDates(List<object> objects)
{
Type myType = objects.GetType();
IList<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());
if(props.Contains(Asset)
{
doStuff();
}
}
I thought if I got the type of object then I could use the correct property but that didn't work. Do I even need to find which type it is?
Asset isn't a valid expression here, unless you've actually got a variable called Asset somewhere. You want to find out if the name of any property is Asset... and you want to do it on each object, not on the list itself:
foreach (var item in objects)
{
var props = item.GetType().GetProperties();
var assetProperty = props.FirstOrDefault(p => p.Name == "Asset");
if (assetProperty != null)
{
var value = assetProperty.GetValue(item, null);
// Do stuff...
}
}
Alternatively, if you're only looking for a public property, you can pass the name to GetProperty:
foreach (var item in objects)
{
var assetProperty = item.GetType().GetProperty("Asset");
if (assetProperty != null)
{
var value = assetProperty.GetValue(item, null);
// Do stuff...
}
}
Having said this, it would be cleaner if you had an interface or something similar:
var assetItems = objects.OfType<IHasAsset>();
foreach (var assetItem in assetItems)
{
var asset = assetItem.Asset;
...
}
If you know what type all of the objects in objects should be, then objects should be a list of that type, instead of a list of object (so List<MyType>). At this point, you can simply refer to objects[0].Asset, since all of the objects in the list are of type MyType.
If you still want objects to be a List<object>, then you'll have to typecast each of the objects in the list to a MyType in order to use the Asset property of the MyType class: ((MyType)objects[0]).Asset.
If you do this, and you aren't sure that all of the objects in the list are actually of type MyType, you need to check that:
if (objects[0] is MyType)
{
// do stuff with ((MyType)objects[0]).Asset
}