Multiple sorting on single index of Algolia - sorting

Can we put multiple sorting/custom ranking on single index of Algolia without creating multiple replica indexes for each sorting.
Lets understand with example suppose my application is SalesManagement and I create 3 replicas MobileSale,LaptopSale and PhoneSale and then in each one I put custom sorting to type = 'mobile',type = 'laptop' and type = 'phone' respectively.
Instead of doing above I just want to create one index and put sorting on compile time by sending different settings by using IndexSetting in my code so I don't need to create multiple indexes

Using ranking property of IndexSetting it worked for me
private readonly SearchClient algoliaClient;
private readonly SearchIndex feedIndex;
public AlgoliaService( )
{
algoliaClient = new SearchClient("app-id", "api-key");
SearchIndex = algoliaClient.InitIndex(_defaultSettings.Algolia.FeedIndex);
algoliaClient.InitIndex("user-index-name");
}
public async Task SetCustomSortingAttributes(string sortBy)
{
if (sortBy != null)
{
List<string> sortingAttributesList = new List<string>();
StringBuilder sortAttribute = new StringBuilder();
if (sortBy == "most_viewed")
{
sortAttribute.Append("desc(most_viewed)");
}
else if (sortBy == "entityUniqueViews" )
{
sortAttribute.Append("desc(entityUniqueViews)");
}
else if (sortBy == "latest")
{
sortAttribute.Append("desc(latest)");
}
else if (sortBy == "price_asc")
{
sortAttribute.Append("asc(price)");
}
else
{
sortAttribute.Append("desc(price)");
}
sortingAttributesList.Add(sortAttribute.ToString());
IndexSettings settings = new IndexSettings
{
Ranking = sortingAttributesList
};
var setIndexSetting = await feedIndex.SetSettingsAsync(settings);
}
}

Related

Calling Dynamics Web API with Entity metadata early binding

I would like to consume my organizations dynamics oData endpoint but with early bound classes. However, there are a lot of early bound tools out there and I wanted to know which one provides the best developer experience/least resistance?
For example, there is this one:
https://github.com/daryllabar/DLaB.Xrm.XrmToolBoxTools
https://github.com/yagasoft/DynamicsCrm-CodeGenerator
and so on. Is there a developer preference/method out there?
Early bound classes are for use with the Organization Service which is a SOAP service. The normal way to generate those classes is using CrmSvcUtil.
OData can be used in Organization Data Service or Web API, but those don't have Early Bound classes.
Further reading: Introducing the Microsoft Dynamics 365 web services
It's not impossible to use with standard SOAP Early bound class. We just have to be creative. If we work just with basic attributes (fields, not relationships, ecc) it seems possible. For example. for create and update, OData will not accept the entire early bounded class, just pass the attibutes:
class Program
{
static void Main(string[] args)
{
string token = System.Threading.Tasks.Task.Run(() => GetToken()).Result;
CRMWebAPI dynamicsWebAPI = new CRMWebAPI("https:/ORG.api.crm4.dynamics.com/api/data/v9.1/",
token);
CRMGetListOptions listOptions = new CRMGetListOptions
{
Select = new string[] { "EntitySetName" },
Filter = "LogicalName eq 'contact'"
};
dynamic entityDefinitions = dynamicsWebAPI.GetList<ExpandoObject>("EntityDefinitions", listOptions).Result;
Contact contact = new Contact
{
FirstName = "Felipe",
LastName = "Test",
MobilePhone = "38421254"
};
dynamic ret = System.Threading.Tasks.Task.Run(async () => await dynamicsWebAPI.Create(entityDefinitions.List[0].EntitySetName, KeyPairValueToObject(contact.Attributes))).Result;
}
public static async Task<string> GetToken()
{
string api = "https://ORG.api.crm4.dynamics.com/";
ClientCredential credential = new ClientCredential("CLIENT_ID", "CLIENT_SECRET");
AuthenticationContext authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/commom/oauth2/authorize");
return authenticationContext.AcquireTokenAsync(api, credential).Result.AccessToken;
}
public static object KeyPairValueToObject(AttributeCollection keyValuePairs)
{
dynamic expando = new ExpandoObject();
var obj = expando as IDictionary<string, object>;
foreach (var keyValuePair in keyValuePairs)
obj.Add(keyValuePair.Key, keyValuePair.Value);
return obj;
}
}
It's a simple approach and I didn't went further.
Maybe we have to serealize other objects as OptionSets, DateTime (pass just the string) and EntityReferences but this simple test worked fine to me. I'm using Xrm.Tools.WebAPI and Microsoft.IdentityModel.Clients.ActiveDirectory. Maybe it's a way.
[Edit]
And so I decided to go and created a not well tested method to cast the attributes. Problems: We have to follow OData statments to use the API. To update/create an entity reference we can use this to reference https://www.inogic.com/blog/2016/02/set-values-of-all-data-types-using-web-api-in-dynamics-crm/
So
//To EntityReference
entityToUpdateOrCreate["FIELD_SCHEMA_NAME#odata.bind"] = "/ENTITY_SET_NAME(GUID)";
So, it's the Schema name, not field name. If you use CamelCase when set you fields name you'll have a problem where. We can resolve that with a (to that cute) code
public static object EntityToObject<T>(T entity) where T : Entity
{
dynamic expando = new ExpandoObject();
var obj = expando as IDictionary<string, object>;
foreach (var keyValuePair in entity.Attributes)
{
obj.Add(GetFieldName(entity, keyValuePair), CastEntityAttibutesValueOnDynamicObject(keyValuePair.Value));
}
return obj;
}
public static object CastEntityAttibutesValueOnDynamicObject(object attributeValue)
{
if (attributeValue.GetType().Name == "EntityReference")
{
CRMGetListOptions listOptions = new CRMGetListOptions
{
Select = new string[] { "EntitySetName" },
Filter = $"LogicalName eq '{((EntityReference)attributeValue).LogicalName}'"
};
dynamic entitySetName = dynamicsWebAPI.GetList<ExpandoObject>("EntityDefinitions", listOptions).Result.List[0];
return $"/{entitySetName.EntitySetName}({((EntityReference)attributeValue).Id})";
}
else if (attributeValue.GetType().Name == "OptionSetValue")
{
return ((OptionSetValue)attributeValue).Value;
}
else if (attributeValue.GetType().Name == "DateTime")
{
return ((DateTime)attributeValue).ToString("yyyy-MM-dd");
}
else if (attributeValue.GetType().Name == "Money")
{
return ((Money)attributeValue).Value;
}
else if (attributeValue.GetType().Name == "AliasedValue")
{
return CastEntityAttibutesValueOnDynamicObject(((AliasedValue)attributeValue).Value);
}
else
{
return attributeValue;
}
}
public static string GetFieldName<T>(T entity, KeyValuePair<string, object> keyValuePair) where T : Entity
{
switch (keyValuePair.Value.GetType().Name)
{
case "EntityReference":
var entityNameList = System.Threading.Tasks.Task.Run(async () => await dynamicsWebAPI.GetEntityDisplayNameList()).Result;
var firstEntity = entityNameList.Where(x => x.LogicalName == entity.LogicalName).FirstOrDefault();
var attrNameList = System.Threading.Tasks.Task.Run(async () => await dynamicsWebAPI.GetAttributeDisplayNameList(firstEntity.MetadataId)).Result;
return attrNameList.Where(x => x.LogicalName == keyValuePair.Key).Single().SchemaName + "#odata.bind";
case "ActivityParty":
throw new NotImplementedException(); //TODO
default:
return keyValuePair.Key;
}
}
Please, note that this approach do not seems fast or good in anyway. It's better if you have all this values as static so we can save some fetches
[Edit 2]
I just found on XRMToolBox a plugin called "Early bound generator for Web API" and it seems to be the best option. Maybe you should give it a try if you're still curious about that. I guess its the best approach.
The final code is this:
static void Main(string[] args)
{
string token = Task.Run(() => GetToken()).Result;
dynamicsWebAPI = new CRMWebAPI("https://ORG.api.crm4.dynamics.com/api/data/v9.1/",
token);
Contact contact = new Contact
{
FirstName = "Felipe",
LastName = "Test",
MobilePhone = "38421254",
new_Salutation = new EntityReference(new_salutation.EntitySetName, new Guid("{BFA27540-7BB9-E611-80EE-FC15B4281C8C}")),
BirthDate = new DateTime(1993, 04, 14),
};
dynamic ret = Task.Run(async () => await dynamicsWebAPI.Create(Contact.EntitySetName, contact.ToExpandoObject())).Result;
Contact createdContact = dynamicsWebAPI.Get<Contact>(Contact.EntitySetName, ret, new CRMGetListOptions
{
Select = new string[] { "*" }
}).Result;
}
and you have to change the ToExpandoObject on Entity.cs class (generated by the plugin)
public ExpandoObject ToExpandoObject()
{
dynamic expando = new ExpandoObject();
var expandoObject = expando as IDictionary<string, object>;
foreach (var attributes in Attributes)
{
if (attributes.Key == GetIdAttribute())
{
continue;
}
var value = attributes.Value;
var key = attributes.Key;
if (value is EntityReference entityReference)
{
value = $"/{entityReference.EntitySetName}({entityReference.EntityId})";
}
else
{
key = key.ToLower();
if (value is DateTime dateTimeValue)
{
var propertyForAttribute = GetPublicInstanceProperties().FirstOrDefault(x =>
x.Name.Equals(key, StringComparison.InvariantCultureIgnoreCase));
if (propertyForAttribute != null)
{
var onlyDateAttr = propertyForAttribute.GetCustomAttribute<OnlyDateAttribute>();
if (onlyDateAttr != null)
{
value = dateTimeValue.ToString(OnlyDateAttribute.Format);
}
}
}
}
expandoObject.Add(key, value);
}
return (ExpandoObject)expandoObject;
}
Links:
https://github.com/davidyack/Xrm.Tools.CRMWebAPI
https://www.xrmtoolbox.com/plugins/crm.webApi.earlyBoundGenerator/
We currently use XrmToolkit which has it's own version of early binding called ProxyClasses but will allow you to generate early binding using the CRM Service Utility (CrmSvcUtil). It does a lot more than just early binding which is why we use it on all of our projects but the early binding features alone would have me sold on it. in order to regenerate an entity definition all you do is right click the cs file in visual studio and select regenerate and it is done in a few seconds.
For my first 3 years of CRM development I used the XrmToolbox "Early Bound Generator" plugin which is really helpful as well.

ASP.NET MVC5 add 2 Sync Times to the Application

I Added the 2 Sync Times to my DB as 2 new columns and inserted values as below:
USE [DB]
ALTER TABLE [dbo].[TableName]
ADD ColumnName2 time, ColumnName3 time
This was for adding the columns.
For inserting the row values I did:
USE DB
INSERT INTO TableName (ColumnName2, ColumnName3)
VALUES ('20:30:00', '23:30:00')
This was the data for the fixed times in the rows of those columns.
I also went through all the layers of the application such as (controller, models, views, queries, services, interfaces, and so forth. NOW when I try to update any of the new times added they default to the first time that already existed on the table as a COLUMN with time in the row.
I could not post the image for the time fields from the application because it is not permitted. However, the image is in a little panel and consists of 3 fields (textboxfor) with a time picker for each one.
Any help would be greatly appreciated.
Thanks
Now I thought I would post some of the example code to see if this helps
// My controller method for those sync times
[HttpPost]
[Page(PageName.UpdateSystemConfigTime)]
public ActionResult UpdateTime(SystemMaintenanceViewModel model)
{
var dateTime = DateTime.ParseExact(model.SystemConfiguration.SynchronizationTime, "h:mm tt", CultureInfo.InvariantCulture);
var dateTime2 = DateTime.ParseExact(model.SystemConfiguration.SynchronizationTime2, "h:mm tt", CultureInfo.InvariantCulture);
var dateTime3 = DateTime.ParseExact(model.SystemConfiguration.SynchronizationTime3, "h:mm tt", CultureInfo.InvariantCulture);
//model.ProcessTime
if (model.SystemConfiguration.SynchronizationTime != null &&
model.SystemConfiguration.SynchronizationTime2 != null &&
model.SystemConfiguration.SynchronizationTime3 != null);
{
var sysConfig = new DTO.SystemSync.SystemConfiguration
{
SyncTime = dateTime.TimeOfDay,
SyncTime2 = dateTime2.TimeOfDay,
SyncTime3 = dateTime3.TimeOfDay
};
configService.UpdateSyncTime(sysConfig);
configService.UpdateSyncTime2(sysConfig);
configService.UpdateSyncTime3(sysConfig);
}
return RedirectToAction("Index");
}
////My Private method
private SystemConfiguration GetSystemConfig()
{
var model = new SystemConfiguration();
var config = configService.GetSyncTime();
configService.GetSyncTime2();
configService.GetSyncTime3();
if (config == null) return model;
var ts = config.SyncTime;
if (ts != null)
{
model.SynchronizationTime = ts.ToString();
}
var ts2 = config.SyncTime2;
if (ts2 != null)
{
model.SynchronizationTime2 = ts2.ToString();
}
var ts3 = config.SyncTime3;
if (ts3 != null)
{
model.SynchronizationTime3 = ts3.ToString();
}
return model;
============================================================================
/// My configuration command
namespace --.--.Commands
{
public class ConfigurationCommand : CommandBase, IConfigurationCommand
{
static ConfigurationCommand()
{
ConfigureAutoMapper();
}
private static void ConfigureAutoMapper()
{
Mapper.CreateMap<SystemConfiguration, entity.SystemConfiguration>()
.ForMember(dest => dest.SyncTime, opt => opt.ResolveUsing<TimeSpanToSqlTimeResolver>())
.ForMember(dest => dest.SyncTime2, opt => opt.ResolveUsing<TimeSpanToSqlTimeResolver>())
.ForMember(dest => dest.SyncTime3, opt => opt.ResolveUsing<TimeSpanToSqlTimeResolver>());
}
public void UpdateSyncTime(SystemConfiguration timeOfDay)
{
Guard.NotNull(timeOfDay);
var mapped = Mapper.Map<entity.SystemConfiguration>(timeOfDay);
var config = Context.SystemConfigurations.SingleOrDefault();
//if this is the first time, then we need to insert
if (config == null)
{
var newConfig = new entity.SystemConfiguration
{
SyncTime = mapped.SyncTime
};
Context.SystemConfigurations.Add(newConfig);
}
else
{
config.SyncTime = mapped.SyncTime;
}
SaveChanges();
}
public void UpdateSyncTime2(SystemConfiguration timeOfDay)
{
Guard.NotNull(timeOfDay);
var mapped = Mapper.Map<entity.SystemConfiguration>(timeOfDay);
var config = Context.SystemConfigurations.SingleOrDefault();
if (config == null)
{
var newConfig = new entity.SystemConfiguration
{
SyncTime2 = mapped.SyncTime2
};
Context.SystemConfigurations.Add(newConfig);
}
else
{
config.SyncTime2 = mapped.SyncTime2;
}
SaveChanges();
}
public void UpdateSyncTime3(SystemConfiguration timeOfDay)
{
Guard.NotNull(timeOfDay);
var mapped = Mapper.Map<entity.SystemConfiguration>(timeOfDay);
var config = Context.SystemConfigurations.SingleOrDefault();
if (config == null)
{
var newConfig = new entity.SystemConfiguration
{
SyncTime3 = mapped.SyncTime3
};
Context.SystemConfigurations.Add(newConfig);
}
else
{
config.SyncTime3 = mapped.SyncTime3;
}
SaveChanges();
}
}
}
=========================================================================================================
// My configuration service
namespace --.--.--.SystemSync
{
public class ConfigurationService : IConfigurationService
{
private IConfigurationQuery query;
private IConfigurationCommand command;
public ConfigurationService(IConfigurationQuery query,IConfigurationCommand command)
{
this.query = query;
this.command = command;
}
public void UpdateSyncTime(SystemConfiguration timeOfDay)
{
command.UpdateSyncTime(timeOfDay);
}
public void UpdateSyncTime2(SystemConfiguration timeOfDay)
{
command.UpdateSyncTime2(timeOfDay);
}
public void UpdateSyncTime3(SystemConfiguration timeOfDay)
{
command.UpdateSyncTime3(timeOfDay);
}
public SystemConfiguration GetSyncTime()
{
return query.GetSyncTime();
}
public SystemConfiguration GetSyncTime2()
{
return query.GetSyncTime2();
}
public SystemConfiguration GetSyncTime3()
{
return query.GetSyncTime3();
}
public List<PageResource> GetPages()
{
return query.GetPages().ToList();
}
}
}
You made a comment about fixed times, Are you looking for something like this?
CREATE TABLE [dbo].[Zamen](
[Id] [int] IDENTITY(1,1) NOT NULL,
[time1] [time](3) NOT NULL,
[time2] [time](3) NOT NULL,
[Content] [varchar](100) NULL,
CONSTRAINT [PK_Zamen] PRIMARY KEY CLUSTERED
(
[Id] ASC
))
GO
ALTER TABLE [dbo].[Zamen] ADD CONSTRAINT [DF_Zamen_time1] DEFAULT (getdate()) FOR [time1]
GO
ALTER TABLE [dbo].[Zamen] ADD CONSTRAINT [DF_Zamen_time2] DEFAULT (getdate()) FOR [time2]
GO
Those alter table statements allow the time to be automatically inserted. So when you do this:
INSERT INTO Zamen (Content) VALUES ('demo')
The current times are placed into the values.
*After seeing the code you added, some input:
In your UpdateTime Action Method, a problem that stands out is you are calling UpdateTimeSync three times, but passing it all three variables each time. I would suggest to refactor your update method -- instead of three update methods, use one update method for all time variables.

Optimal detection of circular reference in object (currently brute forcing)

I have a Product object, which can depend on other products.
This dependency information is stored in a ServiceTemplate object.
These dependencies can be chained (i use the arrow to indicate that 3 depends on 2, and 2 depends on 1):
1 <= 2 <= 3
I need to prevent circular references, where with the above 1 could be set to depend on 3 this causing a loop.
This is hashed up from how i think it may work but is brute forcing the solution - what would be the optimal algorithm approach or is this already the best way?
class Program
{
class Product
{
public Product(int id)
{
this.id = id;
}
private readonly int id;
public int Id { get { return this.id; } }
}
class ServiceTemplate
{
// stores the list of other products that a product depends on
Dictionary<int, List<int>> relationships = new Dictionary<int, List<int>>();
public void SetRequires(Product on, Product requires)
{
if (WouldCauseCircularDepdndency(on.Id, requires.Id) == true)
throw new ArgumentException("circular dependencies not allowed");
List<int> list = null;
this.relationships.TryGetValue(on.Id, out list);
if(list==null)
{
list = new List<int>();
this.relationships.Add(on.Id, list);
}
list.Add(requires.Id);
}
private bool WouldCauseCircularDepdndency(int on, int requires)
{
// get relationships of product we will depend on
List<int> list = null;
this.relationships.TryGetValue(requires, out list);
if (list == null)
{
return false;
}
else if (list.Contains(on))
{
return true;
}
else
{
foreach (var id in list)
{
// traverse each child recursively
if (WouldCauseCircularDepdndency(on, id))
return true;
}
}
return false; // if we got this far, no circular references detected
}
}
static void Main(string[] args)
{
var windows = new Product(1);
var linux = new Product(2);
var mySql = new Product(3);
var ms_sql = new Product(4);
var cluster = new Product(5);
var other = new Product(6);
var config = new ServiceTemplate();
config.SetRequires(mySql, windows); // mySql requires windows
config.SetRequires(mySql, linux); // mySql requires linux
config.SetRequires(ms_sql, windows); // microsoft sql requires windows
config.SetRequires(cluster, ms_sql); // cluster requires microsoft sql
config.SetRequires(other, cluster);
// this should throw an exception due to circular dependency
config.SetRequires(windows, other);
/* at this point the config relationships dictionary is as follows:
3 => 1,2
4 => 1
5 => 4
5 => 6
1 => 6
*/
}
}
You could try topological sorting. If it can be constructed, you have no circular dependency. Otherwise, you have a cycle.
I ended up using the QuickGraph nuget package and an AdjacencyGraph<int,Edge<int>>, once the relationship is added i try and to a TopologicalSort() as advised by #Vesi:
class Program
{
class Product
{
public Product(int id)
{
this.id = id;
}
private readonly int id;
public int Id { get { return this.id; } }
}
class ServiceTemplate
{
// stores the list of other products that a product depends on
AdjacencyGraph<int, Edge<int>> relationshipGraph = new AdjacencyGraph<int, Edge<int>>();
public void SetRequires(Product on, Product requires)
{
var toAdd = new Edge<int>(on.Id, requires.Id);
this.relationshipGraph.AddVerticesAndEdge(toAdd);
try
{
var list = this.relationshipGraph.TopologicalSort();
}
catch (NonAcyclicGraphException)
{
this.relationshipGraph.RemoveEdge(toAdd);
throw new ArgumentException("Circular dependencies not allowed");
}
}
}
static void Main(string[] args)
{
var windows = new Product(1);
var linux = new Product(2);
var mySql = new Product(3);
var ms_sql = new Product(4);
var cluster = new Product(5);
var other = new Product(6);
var config = new ServiceTemplate();
config.SetRequires(mySql, windows); // mySql requires windows
config.SetRequires(mySql, linux); // mySql requires linux
config.SetRequires(ms_sql, windows); // microsoft sql requires windows
config.SetRequires(cluster, ms_sql); // cluster requires microsoft sql
config.SetRequires(other, cluster);
// this should throw an exception due to circular dependency
config.SetRequires(windows, other);
}
}

EmitMapper and List

It's the first time that I use EmitMapper.
I have a list of object ex: Customer and I would like to map this list in a ienumerable of CustomerDTO how can I do that?
Tnx
It's straightforward if you have a list and want to convert it to list of DTOs:
var mapper = ObjectMapperManager.DefaultInstance.GetMapper<Customer, CustomerDTO>();
IEnumerable<CustomerDTO> dtos = listOfCustomer.Select(mapper.map);
The preblem is when the list is in another object, for example User and UserDTO:
class User {
public List<Customer> Customers { get; set; }
}
class UserDTO {
public IEnumerable<CustomerDTO> Customers { get; set; }
}
It seems that EmitMapper does not support conversion from List to Enumerable. A way to support it would be:
var customerMapper = ObjectMapperManager
.DefaultInstance.GetMapper<Customer, CustomerDTO>();
var mapper = ObjectMapperManager.DefaultInstance
.GetMapper<User, UserDTO>(
new DefaultMapConfig()
.ConvertUsing<List<Customer>, IEnumerable<CustomerDTO>>(
a => a.Select(customerMapper.Map))
);
This can be done creating a custom class, implementing the interface "ICustomConverterProvider" and adding a ConvertGeneric to the "DefaultMapConfig".
Looking on the source code of EmitMapper, i found a class named "ArraysConverterProvider", which is the default generic converter from ICollections to Arrays.
Adapting the code from this class to work with IEnumerable collections:
class GenericIEnumerableConverterProvider : ICustomConverterProvider
{
public CustomConverterDescriptor GetCustomConverterDescr(
Type from,
Type to,
MapConfigBaseImpl mappingConfig)
{
var tFromTypeArgs = DefaultCustomConverterProvider.GetGenericArguments(from);
var tToTypeArgs = DefaultCustomConverterProvider.GetGenericArguments(to);
if (tFromTypeArgs == null || tToTypeArgs == null || tFromTypeArgs.Length != 1 || tToTypeArgs.Length != 1)
{
return null;
}
var tFrom = tFromTypeArgs[0];
var tTo = tToTypeArgs[0];
if (tFrom == tTo && (tFrom.IsValueType || mappingConfig.GetRootMappingOperation(tFrom, tTo).ShallowCopy))
{
return new CustomConverterDescriptor
{
ConversionMethodName = "Convert",
ConverterImplementation = typeof(GenericIEnumerableConverter_OneTypes<>),
ConverterClassTypeArguments = new[] { tFrom }
};
}
return new CustomConverterDescriptor
{
ConversionMethodName = "Convert",
ConverterImplementation = typeof(GenericIEnumerableConverter_DifferentTypes<,>),
ConverterClassTypeArguments = new[] { tFrom, tTo }
};
}
}
class GenericIEnumerableConverter_DifferentTypes<TFrom, TTo> : ICustomConverter
{
private Func<TFrom, TTo> _converter;
public IEnumerable<TTo> Convert(IEnumerable<TFrom> from, object state)
{
if (from == null)
{
return null;
}
TTo[] result = new TTo[from.Count()];
int idx = 0;
foreach (var f in from)
{
result[idx++] = _converter(f);
}
return result;
}
public void Initialize(Type from, Type to, MapConfigBaseImpl mappingConfig)
{
var staticConverters = mappingConfig.GetStaticConvertersManager() ?? StaticConvertersManager.DefaultInstance;
var staticConverterMethod = staticConverters.GetStaticConverter(typeof(TFrom), typeof(TTo));
if (staticConverterMethod != null)
{
_converter = (Func<TFrom, TTo>)Delegate.CreateDelegate(
typeof(Func<TFrom, TTo>),
null,
staticConverterMethod
);
}
else
{
_subMapper = ObjectMapperManager.DefaultInstance.GetMapperImpl(typeof(TFrom), typeof(TTo), mappingConfig);
_converter = ConverterBySubmapper;
}
}
ObjectsMapperBaseImpl _subMapper;
private TTo ConverterBySubmapper(TFrom from)
{
return (TTo)_subMapper.Map(from);
}
}
class GenericIEnumerableConverter_OneTypes<T>
{
public IEnumerable<T> Convert(IEnumerable<T> from, object state)
{
if (from == null)
{
return null;
}
return from;
}
}
This code is just a copy with a minimum of adaptation as possible and can be applyed to objects with many levels of hierarchy.
You can use the above code with the following command:
new DefaultMapConfig().ConvertGeneric(
typeof(IEnumerable<>),
typeof(IEnumerable<>),
new GenericIEnumerableConverterProvider());
This saved my day and I hope to save yours too! hehehe

Update a Dictionary value using Linq

I will call AnalyseLinqUpdate()
I think code itself clear..
I have to find behavior for each dictionary value and replace the value with the behavior I get from the method 'GiveBehavior'
void AnalyseLinqUpdate()
{
Dictionary<string, string> rawCollection = new Dictionary<string, string>();
rawCollection.Add("PT-1", "PTC-1");
rawCollection.Add("PT-2", "PTC-1");
rawCollection.Add("PT-3", "PTC-2");
rawCollection.Add("PT-4", "PTC-2");
rawCollection.Add("PT-5", "PTC-3");
rawCollection.Add("PT-6", "PTC-3");
//update here
// call GiveBehavior("PTC-1");
//returns a string that needs to be updated in place of "PTC-1"
}
string GiveBehavior(string ptc)
{
StringComparison ignoreCase = StringComparison.OrdinalIgnoreCase;
ptc = ptc.Trim();
if (ptc.Equals("PTC-1", ignoreCase))
{
return "PTB-1";
}
else if (ptc.Equals("PTC-1", ignoreCase))
{
return "PTB-2";
}
else
{
return "PTB-3";
}
}
Currently I have done like:
List<string> keys = rawCollection.Keys.ToList();
foreach (string key in keys)
{
string behavior = GiveBehavior(rawCollection[key]);
rawCollection[key] = behavior;
}
This is how I update the dictionary..
Is there anyway tat can be done via LINQ...
You could try the following:
List<string> keys = rawCollection.Keys.ToList();
keys.ForEach(key => { rawCollection[key] = GiveBehavior(rawCollection[key]); });
That should do it
rawCollection = rawCollection.ToDictionary(item => item.Key, item => GiveBehavior(item.Key));

Resources