Microsoft Dynamics CRM 2011. Generic method to get entity by ID - dynamics-crm-2011

In CRM 4.0 I had the generic method in my repository:
public T GetEntityById(Guid id)
{
var entities =
from e in m_context.GetEntities(typeof (T).Name)
where e.GetPropertyValue<Guid>(IdentityFieldName) == id
select e;
return (T) entities.FirstOrDefault();
}
But what about CRM 2011? ICrmEntity with GetPropertyValue method is missed...
What is alternate to get generic entity by ID?

something like (T) m_context.Retrieve(typeof (T).Name, id, new ColumnSet()).
See here

Found this question when looking for the same answer, this is how I ended up solving it.
public T GetEntityByID<T>(Guid guid) where T : Entity
{
return (T) (_organizationService.Retrieve((typeof(T)).Name, guid, new ColumnSet()));
}

You really want to be using the ToEntity method, rather than casting. Sometimes the typeof(T).Name will have capitalization differences, so I wrote a helper funciton as well:
/// <summary>
/// Retrieves the Entity of the given type with the given Id, with the given columns
/// </summary>
/// <typeparam name="T">An early bound Entity Type</typeparam>
/// <param name="service">open IOrganizationService</param>
/// <param name="id">Primary Key of Entity</param>
/// <param name="columnSet">Columns to retrieve</param>
/// <returns></returns>
public static T GetEntity<T>(this IOrganizationService service, Guid id, ColumnSet columnSet)
where T : Entity
{
return service.Retrieve(EntityHelper.GetEntityLogicalName<T>(), id, columnSet).ToEntity<T>()
}
public static string GetEntityLogicalName<T>() where T : Entity
{
return GetEntityLogicalName(typeof(T));
}
public static string GetEntityLogicalName(Type type)
{
var field = type.GetField("EntityLogicalName");
if (field == null)
{
if (type == typeof(Entity))
{
return "entity";
}
else
{
throw new Exception("Type " + type.FullName + " does not contain an EntityLogicalName Field");
}
}
return (string)field.GetValue(null);
}

Related

How to use current company in BQL query

I need to know how I can get the user's current company so that I can use it in the Where clause of a BQL query. I'm not talking about the tenant "company". I'm talking about the companies that are defined on screen CS101500. I'm not seeing anything on the AccessInfo that seems to indicate the current Company. The current Branch ID is there but, I need the company to which the branch belongs.
Using version 21.203
TIA!
This will require the creation of a custom BQL element class.
IBqlOperand - A BQL Scalar operand
IBqlCreator - A Bql elements creator that requires implementation of AppendExpression and Verify methods.
public class CurrentOrganization : IBqlCreator, IBqlOperand
{
#region Methods
#region AppendExpression
/// <summary>
/// Appends the SQL tree expression that corresponds to the BQL command to an SQL tree query.
/// </summary>
/// <param name="exp">The SQL tree expression to be appended.</param>
/// <param name="graph">A graph instance.</param>
/// <param name="info">The information about the BQL command.</param>
/// <param name="selection">The fragment of the BQL command that is translated to an SQL tree expression.</param>
/// <returns></returns>
public bool AppendExpression(ref SQLExpression exp, PXGraph graph, BqlCommandInfo info, BqlCommand.Selection selection)
{
if (graph == null || !info.BuildExpression)
{
return true;
}
PXMutableCollection.AddMutableItem(this);
exp = new SQLConst(graph.BranchOrganizationID());
return true;
}
#endregion
#region Verify
/// <summary>
/// No Clue.
/// </summary>
/// <param name="cache"></param>
/// <param name="item"></param>
/// <param name="pars"></param>
/// <param name="result"></param>
/// <param name="value"></param>
public void Verify(PXCache cache, object item, List<object> pars, ref bool? result, ref object value)
{
value = cache.Graph.BranchOrganizationID();
}
#endregion
#endregion
}
The method call .BranchOrganizationID() is a PXGraph extension method that returns the OrganizationID from a Branch IPrefetchable
Calls to this prefetchable can be replaced with a BQL PXSelect :
PXSelect<Branch, Where<Branch.branchID,
Equal<Current<AccessInfo.branchID>>>>.Select(...)
Example usage would be as follows :
[PXDefault(typeof(Search<Branch.branchID,Where<Branch.organizationID,Equal<CurrentOrganization>>>))]
You can try to use this code
public int? GetOrganizationID(PXGraph graph, int? branchID)
{
int? accountID = ((Organization)OrganizationMaint.FindOrganizationByID(graph, PXAccess.GetParentOrganizationID(branchID))).BAccountID;
return accountID;
}
This is the final code that got what I needed, per Josh's solution. Van Hoesen for the win!!
public class CurrentOrganization : IBqlCreator, IBqlOperand
{
public virtual bool AppendExpression(ref SQLExpression exp, PXGraph graph, BqlCommandInfo info, BqlCommand.Selection selection)
{
if ((graph == null) || (!info.BuildExpression))
return true;
PXMutableCollection.AddMutableItem(this);
//exp = new SQLConst(graph.BranchOrganizationID());
Branch branch = PXSelect<Branch, Where<Branch.branchID, Equal<Current<AccessInfo.branchID>>>>.Select(graph);
exp = new SQLConst( branch.OrganizationID);
return true;
}
public void Verify(PXCache cache, object item, List<object> pars, ref bool? result, ref object value)
{
Branch branch = PXSelect<Branch, Where<Branch.branchID, Equal<Current<AccessInfo.branchID>>>>.Select(cache.Graph);
value = branch.OrganizationID;
//value = cache.Graph.BranchOrganizationID();
}
}

Quartz.net RFC 2445 or RFC 5545 instead of CRON

We have a web server running in .NET which uses Quartz to schedule jobs.
The triggers for the jobs are provided in RFC 2445 format, but Quartz uses the CRON format. I would now like to either
A: Find a library which can convert my RFC 2445 rule to a CRON Rule
B: Rather, give Quartz the RFC rule.
In the latter case, I found some Java libraries but not for .NET.
I also tried writing my own library but I'm stuck with intervals. An RFC2445 rule can define a biweekly (or triweekly or n-weekly) job with
FREQ=WEEKLY;BYDAY=MO;INTERVAL=2
I.e. Every other monday. Yet CRON does not seem to have this functionality.
I have a similar requirement and I couldn't find a RFC 5545 compliant library to work with Quartz scheduler and ended up implementing a custom trigger myself following this suggestion
In my case we are using Telerik Scheduler control to populate the RRULE but you could probably do the same with iCal.Net library as well. This is not the full implementation here but it will get you started and the code is UNTESTED.
Another note: "FREQ=WEEKLY;BYDAY=MO;INTERVAL=2" will probably fail if you try to parse it using Telerik RecurrenceRule, since it's missing DTSTART, DTEND etc. This is an example of a recurrence rule string that will not fail: "DTSTART:20210309T050000Z\r\nDTEND:20210309T060000Z\r\nRRULE:FREQ=WEEKLY;BYDAY=TU;INTERVAL=1".
You need to implement ITrigger interface. A lot of it can be copied from CroneTriggerImpl class and modified.
public interface IRRuleTrigger : ITrigger
{
string RecurrenceRuleString { get; set; }
}
Then you need an implementation class inherited from AbstractTrigger
public class MyTriggerImpl: AbstractTrigger, IRRuleTrigger
{
//implement all members here. Look at CronTriggerImpl class in Quartz.Net source. I'm pasting some of the implementation code but not all.
//...
private RecurrenceRule rRule;
/// <summary>
/// Gets or sets the RRULE expression string.
/// </summary>
/// <value>The expression string.</value>
public string RecurrenceRuleString
{
set
{
TimeZoneInfo originalTimeZone = TimeZone;
var success = RecurrenceRule.TryParse(value, out var parsedRule);
if(success) rRule = parsedRule ;// RecurrenceRule(value!);
}
get => rRule?.ToString();
}
/// <summary>
/// Gets or sets the RRULE expression string.
/// </summary>
/// <value>The expression string like RRULE:FREQ=WEEKLY;BYDAY=MO;INTERVAL=2.</value>
public string RecurrenceRuleString
{
set
{
TimeZoneInfo originalTimeZone = TimeZone;
var success = RecurrenceRule.TryParse(value, out var parsedRule);
if(success) rRule = parsedRule ;// RecurrenceRule(value!);
}
get => rRule?.ToString();
}
////////////////////////////////////////////////////////////////////////////
//
// Computation Functions
//
////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Gets the next time to fire after the given time.
/// </summary>
/// <param name="afterTime">The time to compute from.</param>
/// <returns></returns>
protected DateTimeOffset? GetTimeAfter(DateTimeOffset afterTime)
{
return rRule?.HasOccurrences == true ?
rRule?.Occurrences.Where(o => o > afterTime).Min()
: null;
}
/// <summary>
/// Returns the time before the given time
/// that this <see cref="IRRuleTrigger" /> will fire.
/// </summary>
/// <param name="date">The date.</param>
/// <returns></returns>
protected DateTimeOffset? GetTimeBefore(DateTimeOffset? date)
{
return rRule?.HasOccurrences == true ?
rRule?.Occurrences.Where(o=> o < date).Max()
: null;
}
}
public class RRuleScheduleBuilder : ScheduleBuilder<IRRuleTrigger>
{
private int misfireInstruction = MisfireInstruction.SmartPolicy;
private RecurrenceRule recurrenceRule;
public override IMutableTrigger Build()
{
MyTriggerImpl myTriggerImpl = new MyTriggerImpl();
myTriggerImpl.MisfireInstruction = misfireInstruction;
myTriggerImpl.RecurrenceRuleString = this.recurrenceRule.ToString();
return myTriggerImpl;
}
/// <summary>
/// Create a RRuleScheduleBuilder with the given string expression - which
/// is presumed to be valid expression (and hence only a RuntimeException
/// will be thrown if it is not).
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="recurrenceRuleString">the RRule expression to base the schedule on.</param>
/// <returns>the new RRuleScheduleBuilder</returns>
public static RRuleScheduleBuilder RecurrenceRuleSchedule(string recurrenceRuleString)
{
var success = RecurrenceRule.TryParse(recurrenceRuleString, out var rRule);
if(!success) throw new ArgumentException($"Recurrence Rule String ({recurrenceRuleString}) is invalid.");
return new RRuleScheduleBuilder(rRule);
}
protected RRuleScheduleBuilder(RecurrenceRule rule)
{
this.recurrenceRule = rule ?? throw new ArgumentNullException(nameof(rule), "recurrenceRule cannot be null");
}
}
/// <summary>
/// Extension methods that attach <see cref="RRuleScheduleBuilder" /> to <see cref="TriggerBuilder" />.
/// </summary>
public static class RRuleScheduleTriggerBuilderExtensions
{
public static TriggerBuilder WithRRuleSchedule(this TriggerBuilder triggerBuilder, string recurrenceRuleString)
{
RRuleScheduleBuilder builder = RRuleScheduleBuilder.RecurrenceRuleSchedule(recurrenceRuleString);
return triggerBuilder.WithSchedule(builder);
}
public static TriggerBuilder WithRRuleSchedule(this TriggerBuilder triggerBuilder, string recurrenceRuleString, Action<RRuleScheduleBuilder> action)
{
RRuleScheduleBuilder builder = RRuleScheduleBuilder.RecurrenceRuleSchedule(recurrenceRuleString);
action(builder);
return triggerBuilder.WithSchedule(builder);
}
}
After implementing that, you can create and use your trigger like this:
// Grab the Scheduler instance from the Factory
StdSchedulerFactory factory = new StdSchedulerFactory();
var scheduler = await factory.GetScheduler();
await scheduler.Start();
var job = JobBuilder.Create<MyBusinessClassThatImplementsIJobInterface>()
.WithIdentity("someIdentity", "someGroupName")
.Build();
var trigger = (IRRuleTrigger)TriggerBuilder.Create()
.WithIdentity("someName", "myGroup")
.WithRRuleSchedule(rule.ToString())
.Build();
await scheduler.ScheduleJob(job, trigger);

cannot be able to use "as" key word in DynamicTableEntity (Azure Table)

I have an Azure table where I have inserted heterogeneous entities. After the retrieval, I want to convert them to some specific type using "as". I tried to do this, but it threw the following error:
Cannot be able to convert DynamicTableEntity to TestingEntity Via reference conversion, boxing conversion, unboxing conversion, wrapping conversion or null type conversion.
Is there any way I can convert my entities to a particular type?
My code is as follows:
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));
// Create the table client.
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
CloudTable table = tableClient.GetTableReference("TestingWithTableDatetime");
// Create the table if it doesn't exist.
table.CreateIfNotExists();
TableQuery<DynamicTableEntity> entityQuery =
new TableQuery<DynamicTableEntity>();
var employees = table.ExecuteQuery(entityQuery);
IEnumerable<DynamicTableEntity> entities = table.ExecuteQuery(entityQuery);
foreach (var e in entities)
{
EntityProperty entityTypeProperty;
if (e.Properties.TryGetValue("EntityType", out entityTypeProperty))
{
if (entityTypeProperty.StringValue == "SampleEntity1")
{
//Cannot be able to Use as
var TestingWithTableDatetime = e as SampleEntity1;
}
if (entityTypeProperty.StringValue == "SampleEntity2")
{
// Use entityTypeProperty, RowKey, PartitionKey, Etag, and Timestamp
}
if (entityTypeProperty.StringValue == "SampleEntity3")
{
// Use entityTypeProperty, RowKey, PartitionKey, Etag, and Timestamp
}
}
}
Class definition for Sample1
public class Sample1 : TableEntity
{
public Sample1(string pk, string rk)
{
this.PartitionKey = pk;
this.RowKey = rk;
EntityType = "MonitoringResources";
}
public string EntityType { get; set; }
public Sample1()
{
}
}
Things I have tried.I have created a class as Testing and in that I inherited Table entity.Then Testing is inherited by sample1 as follow
Testing Class definition
public class testing : TableEntity
{
public testing(string pk, string rk)
{
this.PartitionKey = pk;
this.RowKey = rk; //MetricKey
}
public string EntityType { get; set; }
public testing()
{
}
}
modified Class sample1:
public class sample1 : testing
{
public sample1(string pk, string rk) : base(pk, rk)
{
EntityType = "sample1";
}
public sample1()
{
}
}
In this i didnt get any error but
when I am converting it to sample1 by using "as" it returns as null.
Finally I ended with creating some helper.
public static class AzureManager
{
/// <summary>
/// Converts a dynamic table entity to .NET Object
/// </summary>
/// <typeparam name="TOutput">Desired Object Type</typeparam>
/// <param name="entity">Dynamic table Entity</param>
/// <returns>Output Object</returns>
public static TOutput ConvertTo<TOutput>(DynamicTableEntity entity)
{
return ConvertTo<TOutput>(entity.Properties, entity.PartitionKey, entity.RowKey);
}
/// <summary>
/// Convert a Dynamic Table Entity to A POCO .NET Object.
/// </summary>
/// <typeparam name="TOutput">Desired Object Types</typeparam>
/// <param name="properties">Dictionary of Table Entity</param>
/// <returns>.NET object</returns>
public static TOutput ConvertTo<TOutput>(IDictionary<string, EntityProperty> properties, string partitionKey, string rowKey)
{
var jobject = new JObject();
properties.Add("PartitionKey", new EntityProperty(partitionKey));
properties.Add("RowKey", new EntityProperty(rowKey));
foreach (var property in properties)
{
WriteToJObject(jobject, property);
}
return jobject.ToObject<TOutput>();
}
public static void WriteToJObject(JObject jObject, KeyValuePair<string, EntityProperty> property)
{
switch (property.Value.PropertyType)
{
case EdmType.Binary:
jObject.Add(property.Key, new JValue(property.Value.BinaryValue));
return;
case EdmType.Boolean:
jObject.Add(property.Key, new JValue(property.Value.BooleanValue));
return;
case EdmType.DateTime:
jObject.Add(property.Key, new JValue(property.Value.DateTime));
return;
case EdmType.Double:
jObject.Add(property.Key, new JValue(property.Value.DoubleValue));
return;
case EdmType.Guid:
jObject.Add(property.Key, new JValue(property.Value.GuidValue));
return;
case EdmType.Int32:
jObject.Add(property.Key, new JValue(property.Value.Int32Value));
return;
case EdmType.Int64:
jObject.Add(property.Key, new JValue(property.Value.Int64Value));
return;
case EdmType.String:
jObject.Add(property.Key, new JValue(property.Value.StringValue));
return;
default:
return;
}
}
}
the above one works for me.
var obj= AzureManager.ConvertTo<Sample1>(e);
If you find any other way.Please suggest.
Here is an alternative and much simpler solution for you that is natively supported by Azure Storage SDK version > 8.0.0. You do not even need to write any transformation / conversion code :)
Have a look at:
TableEntity.Flatten method: https://msdn.microsoft.com/en-us/library/azure/mt775434.aspx
TableEntity.ConvertBack method: https://msdn.microsoft.com/en-us/library/azure/mt775432.aspx
These methods are provided by the SDK as static, standalone helper methods. Flatten method will convert your entities to a flat dictionary of entity properties where you can simply assign a partition key and row key, create a dynamictableentity from the flat dictionary and write to azure table storage.
When you want to read the entity back, read it as dynamic table entity and pass the property dictionary of the returned dynamic table entity to TableEntity.ConvertBack method. Just tell it which type of object you want the method to convert the property dictionary into, via its generic type parameter and it will do the conversion for you.
I originally implemented these api s as nuget packages and now they are integrated into azure storage sdk. If you want to read a bit more about how they work you can see the article I wrote originally about the nuget packages here:
https://doguarslan.wordpress.com/2016/02/03/writing-complex-objects-to-azure-table-storage/
Is there any way I can convert my entities to a particular type?
We could use DynamicTableEntityConverter to do that.
According to your code, we could use the following code to covert DynamicTableEntity to Sample1
var TestingWithTableDatetime = DynamicTableEntityConverter.ConvertToPOCO<Sample1>(e);

Is there mongodb C# driver support System.Dynamic.DynamicObject in .NET 4?

Im working on a project that use .NET Razor and mongodb. I would like to do something like this:
#{
var feeds = DP.Database.GetCollection("feeds").FindAll();
}
<ul>
#foreach (dynamic feed in feeds)
{
<li>#feed.message - #feed.from.name</li>
}
</ul>
However, the current mongodb C# driver FindAll() return collection of BsonDocument which does not support dynamic object. Anybody know a .NET 4 dynamic supported mongodb C# driver?
Thanks a lot
I created a straight-forward extension to the MongoDB driver that re-serializes the BSON document using Json.NET and deserializes it as a dynamic. By including the following class, you can simply convert your MongoDB queries to dynamic like this
dynamic obj = collection.FindOneByIdAs<BsonDocument>(someObjectId).ToDynamic();
Extension class:
public static class MongoDynamic
{
private static System.Text.RegularExpressions.Regex objectIdReplace = new System.Text.RegularExpressions.Regex(#"ObjectId\((.[a-f0-9]{24}.)\)", System.Text.RegularExpressions.RegexOptions.Compiled);
/// <summary>
/// deserializes this bson doc to a .net dynamic object
/// </summary>
/// <param name="bson">bson doc to convert to dynamic</param>
public static dynamic ToDynamic(this BsonDocument bson)
{
var json = objectIdReplace.Replace(bson.ToJson(), (s) => s.Groups[1].Value);
return Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(json);
}
}
Be sure to reference Newtonsoft.Json.dll (http://james.newtonking.com/projects/json-net.aspx)
Currently, there is no support for dynamic in the MongoDB driver. This is because it is based on .NET 3.5. However, since a .NET 4.0 assembly can reference a .NET 3.5 assembly, it is possible for you to write a IBsonSerializationProvider and an IBsonSerializer in .NET 4.0 to support dynamics.
We, 10gen, are looking at doing this in the future. I have spiked some support at https://github.com/craiggwilson/mongo-csharp-driver/tree/dynamic if you want to take a look. There are most definitely bugs, but it shows that it is possible.
I have a clean solution using custom IBsonSerializer and Newtonsoft.Json.
Setup your custom serializer on the BsonClassMap
map.MapProperty(member => member.Data)
.SetElementName("Data")
.SetSerializer(new DynamicSerializer());
or on the property
[BsonSerializer(typeof(DynamicSerializer))]
public dynamic Data { get; set; }
And just include the following class
public class DynamicSerializer : IBsonSerializer
{
#region Implementation of IBsonSerializer
public object Deserialize(BsonReader bsonReader, Type nominalType, IBsonSerializationOptions options)
{
return Deserialize(bsonReader, nominalType, null, options);
}
public object Deserialize(BsonReader bsonReader, Type nominalType, Type actualType,
IBsonSerializationOptions options)
{
if (bsonReader.GetCurrentBsonType() != BsonType.Document) throw new Exception("Not document");
var bsonDocument = BsonSerializer.Deserialize(bsonReader, typeof(BsonDocument), options) as BsonDocument;
return JsonConvert.DeserializeObject<dynamic>(bsonDocument.ToJson());
}
public IBsonSerializationOptions GetDefaultSerializationOptions()
{
return new DocumentSerializationOptions();
}
public void Serialize(BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options)
{
var json = (value == null) ? "{}": JsonConvert.SerializeObject(value);
BsonDocument document = BsonDocument.Parse(json);
BsonSerializer.Serialize(bsonWriter, typeof(BsonDocument), document,options);
}
#endregion
}
InfoSlips - GlobalKinetic
Just to build on Maximilian's answer. This will return a list of dynamics from any query.
/// <summary>
/// deserializes this BsonDocument cursor result to a list of .net dynamic objects
/// </summary>
/// <param name="cursor">cursor result to convert to dynamic</param>
/// <returns></returns>
public static List<dynamic> ToDynamicList(this MongoCursor<BsonDocument> cursor)
{
var dynamicList = new List<dynamic>();
var list = cursor.ToList();
for (int i = 0, l = list.Count; i < l; i++)
dynamicList.Add(list[i].ToDynamic());
return dynamicList;
}
Even though this is an old topic its still just as relevant today as when post was made, and I have yet to see any solutions out there that provides a simple solution for two way support, I have modified #Maximilian Scherer code so that it lets you convert to dynamic objects which easily let you save your Document again.
public static class MongoDynamic
{
/// <summary>
/// deserializes this bson doc to a .net dynamic object
/// </summary>
/// <param name="bson">bson doc to convert to dynamic</param>
public static dynamic ToDynamic(this BsonDocument bson)
{
var json = bson.ToJson(new MongoDB.Bson.IO.JsonWriterSettings { OutputMode = JsonOutputMode.Strict });
dynamic e = Newtonsoft.Json.JsonConvert.DeserializeObject<ExpandoObject>(json);
BsonValue id;
if (bson.TryGetValue("_id", out id))
{
// Lets set _id again so that its possible to save document.
e._id = new ObjectId(id.ToString());
}
return e;
}
}
Example of usage:
// Get BsonDocument from db here
BsonDocument doc = ...
// Convert to dynamic.
var d = doc.ToDynamic();
// Lets add a none existant property.
d.Name = "test";
// Assuming you already have your collection set up
collection.Save(new BsonDocument(d));

Merge two objects to produce third using AutoMapper

I know it's AutoMapper and not AutoMerge(r), but...
I've started using AutoMapper and have a need to Map A -> B, and to add some properties from C so that B become a kind of flat composite of A + C.
Is this possible in AutoMapper of should I just use AutoMapper to do the heavy lifting then manually map on the extra properties?
Would this not work?
var mappedB = _mapper.Map<A,B>(aInstance);
_mapper.Map(instanceC,mappedB);
You can do this with the ValueInjecter
a.InjectFrom(b)
.InjectFrom(c)
.InjectFrom<SomeOtherMappingAlgorithmDefinedByYou>(dOrBOrWhateverObject);
I searched hard and long on this question and ended up implementing an extension method that merge's objects together.
I reference the steps on my blog http://twistyvortek.blogspot.com and here's the code:
using System;
namespace Domain.Models
{
public static class ExtendedMethods
{
/// <summary>
/// Merges two object instances together. The primary instance will retain all non-Null values, and the second will merge all properties that map to null properties the primary
/// </summary>
/// <typeparam name="T">Type Parameter of the merging objects. Both objects must be of the same type.</typeparam>
/// <param name="primary">The object that is receiving merge data (modified)</param>
/// <param name="secondary">The object supplying the merging properties. (unmodified)</param>
/// <returns>The primary object (modified)</returns>
public static T MergeWith<T>(this T primary, T secondary)
{
foreach (var pi in typeof (T).GetProperties())
{
var priValue = pi.GetGetMethod().Invoke(primary, null);
var secValue = pi.GetGetMethod().Invoke(secondary, null);
if (priValue == null || (pi.PropertyType.IsValueType && priValue == Activator.CreateInstance(pi.PropertyType)))
{
pi.GetSetMethod().Invoke(primary, new[] {secValue});
}
}
return primary;
}
}
}
Usage includes method chaining so you can merge multiple objects into one.
What I would do is use automapper to map part of the properties from your various sources into the same class of DTOs, etc. and then use this extension method to merge them together.
var Obj1 = Mapper.Map(Instance1);
var Obj2 = Mapper.Map(Instance2);
var Obj3 = Mapper.Map(Instance3);
var Obj4 = Mapper.Map(Instance4);
var finalMerge = Obj1.MergeWith(Obj2)
.MergeWith(Obj3)
.MergeWith(Obj4);
Hope this helps someone.
From what I remember with AutoMapper you have to define your mappings as one input to one output (maybe this has changed since - haven't utilized it for many a month).
If this is the case, maybe your mapping should be of KeyValuePair<A,C> (or some sort of object composing both A & C) => B
This way you can have one defined input parameter mapping to your outputted object
There is a nice example of merging multiple sources into a destination using autoMapper, here in Owain Wraggs' EMC Consulting Blog.
EDIT: To guard against the old "dead-link" syndrome, the essence of the code in Owain's blog is below.
/// <summary>
/// Helper class to assist in mapping multiple entities to one single
/// entity.
/// </summary>
/// <remarks>
/// Code courtesy of Owain Wraggs' EMC Consulting Blog
/// Ref:
/// http://consultingblogs.emc.com/owainwragg/archive/2010/12/22/automapper-mapping-from-multiple-objects.aspx
/// </remarks>
public static class EntityMapper
{
/// <summary>
/// Maps the specified sources to the specified destination type.
/// </summary>
/// <typeparam name="T">The type of the destination</typeparam>
/// <param name="sources">The sources.</param>
/// <returns></returns>
/// <example>
/// Retrieve the person, address and comment entities
/// and map them on to a person view model entity.
///
/// var personId = 23;
/// var person = _personTasks.GetPerson(personId);
/// var address = _personTasks.GetAddress(personId);
/// var comment = _personTasks.GetComment(personId);
///
/// var personViewModel = EntityMapper.Map<PersonViewModel>(person, address, comment);
/// </example>
public static T Map<T>(params object[] sources) where T : class
{
// If there are no sources just return the destination object
if (!sources.Any())
{
return default(T);
}
// Get the inital source and map it
var initialSource = sources[0];
var mappingResult = Map<T>(initialSource);
// Now map the remaining source objects
if (sources.Count() > 1)
{
Map(mappingResult, sources.Skip(1).ToArray());
}
// return the destination object
return mappingResult;
}
/// <summary>
/// Maps the specified sources to the specified destination.
/// </summary>
/// <param name="destination">The destination.</param>
/// <param name="sources">The sources.</param>
private static void Map(object destination, params object[] sources)
{
// If there are no sources just return the destination object
if (!sources.Any())
{
return;
}
// Get the destination type
var destinationType = destination.GetType();
// Itereate through all of the sources...
foreach (var source in sources)
{
// ... get the source type and map the source to the destination
var sourceType = source.GetType();
Mapper.Map(source, destination, sourceType, destinationType);
}
}
/// <summary>
/// Maps the specified source to the destination.
/// </summary>
/// <typeparam name="T">type of teh destination</typeparam>
/// <param name="source">The source.</param>
/// <returns></returns>
private static T Map<T>(object source) where T : class
{
// Get thr source and destination types
var destinationType = typeof(T);
var sourceType = source.GetType();
// Get the destination using AutoMapper's Map
var mappingResult = Mapper.Map(source, sourceType, destinationType);
// Return the destination
return mappingResult as T;
}
}
The resultant calling code is nice an succinct.
public ActionResult Index()
{
// Retrieve the person, address and comment entities and
// map them on to a person view model entity
var personId = 23;
var person = _personTasks.GetPerson(personId);
var address = _personTasks.GetAddress(personId);
var comment = _personTasks.GetComment(personId);
var personViewModel = EntityMapper.Map<PersonViewModel>(person, address, comment);
return this.View(personViewModel);
}

Resources