Can I create a MemberInitExpression or any expression of a parameterless constructor from a function that literally returns a parameterless constructor?
public IQueryable<GroupView> GetViewSelect(IQueryable<ItemGroup> myQ)
{
return myQ.SelectMany(GetExpression1(), GetExpression2());
}
internal static Expression<Func<ItemGroup, IEnumerable<ItemDetail>>>
GetExpression1()
{
// m is the ItemGroup
// m.item is Item
// m.item.itemDetail is a collection of ItemDetail
return m => m.item.itemDetail;
}
internal static Expression<Func<ItemGroup, ItemDetail, GroupView>>
GetExpression2()
{
// m is the ItemGroup
// n is the ItemDetail
// and it's error at runtime coz LINQ doesnt know the GetGroupView.. :(
return (m, n) => GetGroupView(m, n);
}
internal static GroupView GetGroupView(ItemGroup m, ItemDetail n)
{
// I think the same error will occurs
// coz LINQ doesnt know the GetItemView and GetItemDetailView
return new GroupView
{
Id = m.id,
Name = m.name,
ItemDetailsTotalCount =
m.item.ItemDetails.Sum(nn => nn.item.itemDetails.Count),
Item = GetItemView(m.item),
ItemDetail = GetItemDetailView(n)
};
}
internal static ItemView GetItemView(Item m)
{
return new ItemView
{
Id = m.id,
Name = m.name,
DetailCount = m.ItemDetail.Count
};
}
internal static ItemDetailView GetItemDetailView(ItemDetail n)
{
return new ItemDetailView
{
Id = n.id,
Name = n.name,
Supplier = GetSupplierView(n.supplier)
};
}
internal static SupplierView GetSupplierView(Supplier n)
{
return new SupplierView
{
Id = n.id,
Name = n.name,
Email = n.email ?? "no email",
Phone = n.phone ?? "no phone"
};
}
Of course none of those above works,.. but I just want to avoid retyping the same parameterless constructor over and over in order to get the view constructor every time I want to get different view..
For example, I'd like to call it like this
public IQueryable<ItemView> GetViewSelect(IQueryable<Item> myQ)
{
return myQ.Select(GetExpression3());
}
public IQueryable<ItemView> GetViewSelect(IQueryable<ItemDetail> myQ)
{
return myQ.Select(GetExpression3());
}
internal static Expression<Func<Item, ItemView>> GetExpression3()
{
return m => GetItemView(m);
}
internal static Expression<Func<ItemDetail, ItemView>> GetExpression4()
{
return m => GetItemView(m.item);
}
instead of writing the same parameterless constructor everytime I call it like this below..
internal static Expression<Func<Item, ItemView>> GetExpression3()
{
// writing same parameterless constructor again
return m => new ItemView
{
Id = m.id,
Name = m.name,
DetailCount = m.ItemDetail.Count
};
}
internal static Expression<Func<ItemDetail, ItemView>> GetExpression4()
{
// the same thing again
return m => new ItemView
{
Id = m.item.id,
Name = m.item.name,
DetailCount = m.item.ItemDetail.Count
};
}
internal static Expression<Func<ItemGroup, ItemDetail, GroupView>>
GetExpression2()
{
return (m, n) => new GroupView
{
Id = m.id,
Name = m.name,
ItemDetailsTotalCount =
m.item.ItemDetails.Sum(nn => nn.item.itemDetails.Count),
Item = new ItemView
{
Id = m.item.id,
Name = m.item.name,
DetailCount = m.item.ItemDetail.Count
},
ItemDetail = new ItemDetailView
{
Id = n.id,
Name = n.name,
Supplier = new SupplierView
{
Id = n.id,
Name = n.name,
Email = n.email ?? "no email",
Phone = n.phone ?? "no phone"
}
}
};
}
So I was actually looking for a way to convert
new ItemView
{
Id = m.id,
Name = m.name,
DetailCount = m.ItemDetail.Count
}
into a MemberInitExpression parameterless constructor so maybe I can use it like below without doing any member-assignment..
internal Expression<Func<Item, ItemView>> GetExpression4e
(ParameterExpression m)
{
// looking for something like this..
MemberInitExpression ItemViewInitExpression =
Something("GetItemView", new Object[] {m});
return Expression.Lambda<Func<Item, ItemView>>
(ItemViewInitExpression, m);
}
internal static Expression<Func<Item, ItemView>> GetExpression3()
{
// writing same parameterless constructor again
return m => new ItemView
{
Id = m.id,
Name = m.name,
DetailCount = m.ItemDetail.Count
};
}
This method already produces a MemberInitExpression. If you do
var type = GetExpression3().Body.GetType();
You'll get System.Linq.Expressions.MemberInitExpression
If all you want is to pass some parameters, then pass some parameters:
internal static Expression<Func<Item, ItemView>> GetExpression3(int id, string name, int count)
{
// writing same parameterless constructor again
return m => new ItemView
{
Id = id,
Name = name,
DetailCount = count
};
}
Related
I m trying to run below code and getting error can not implicitly convert string to int CS0029
on code new Customer { Name ="Cust one"} ,
new Customer { Name = "Customer2" }
find below full code
public ActionResult Random() // Random is action result or object name to be made any action
{
var customers = new List<Customer>
{
new Customer { Name ="Cust one"} ,
new Customer { Name = "Customer2" }
};
var viewModel = new RandomMovieViewModel
{
Movie = movie,
Customers = customers
};
I want to validate rule against input array data with runtime indexer not with some fixed zero index value.
It works if i insert data one by one in session.Insert()
It does't work if i use sesion.InsertAll() with array data
I tried providing Expression.Constant value to indexer but no action triggers
class Program
{
static void Main(string[] args)
{
RuleTestWithSingleInsertData();
// RuleTestWithInsertDataAll();
Console.ReadKey();
}
public static void RuleTestWithSingleInsertData()
{
try
{
CustomRuleRepository repository = new CustomRuleRepository();
List<RuleEngineEntity> rules = new List<RuleEngineEntity>();
rules.Add(new RuleEngineEntity { FieldName = "Age", Name = "CustomerCheck", Value = 25 });
repository.LoadRuleForTest1(rules.FirstOrDefault());
//Compile rules
var factory = repository.Compile();
//Create a working session
var session = factory.CreateSession();
RuleEngineRequestModel ruleEngineRequestModel = new RuleEngineRequestModel
{
ruleList = rules,
customerData = new List<Customer>() { new Customer { Name = "A", Age = 19 },
new Customer { Name = "B", Age = 26 } }.ToArray()
};
session.InsertAll(ruleEngineRequestModel.customerData);
var IspassedorNot = session.Fire();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
public static void RuleTestWithInsertDataAll()
{
try
{
CustomRuleRepository repository = new CustomRuleRepository();
List<RuleEngineEntity> rules = new List<RuleEngineEntity>();
rules.Add(new RuleEngineEntity { FieldName = "Age", Name = "CustomerCheck", Value = 25 });
repository.LoadRuleForTest2(rules.FirstOrDefault());
//Compile rules
var factory = repository.Compile();
//Create a working session
var session = factory.CreateSession();
RuleEngineRequestModel ruleEngineRequestModel = new RuleEngineRequestModel
{
ruleList = rules,
customerData = new List<Customer>() { new Customer { Name = "A", Age = 28 },
new Customer { Name = "B", Age = 26 } }.ToArray()
};
session.InsertAll(ruleEngineRequestModel.customerData);
var IspassedorNot = session.Fire();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
public class RuleEngineRequestModel
{
public List<RuleEngineEntity> ruleList { get; set; }
public Customer[] customerData { get; set; }
public List<Customer> customerDataList { get; set; }
}
public class RuleEngineEntity
{
public string Name { get; set; }
public int Value { get; set; }
public string Operator { get; set; }
public string FieldName { get; set; }
public bool SendEmail { get; set; }
}
public class Customer
{
public string Name { get; set; }
public int Age { get; set; }
}
public class CustomRuleRepository : IRuleRepository
{
private readonly IRuleSet _ruleSet = new RuleSet("customerRule");
public IEnumerable<IRuleSet> GetRuleSets()
{
return new[] { _ruleSet };
}
public void LoadRuleForTest1(RuleEngineEntity rule)
{
_ruleSet.Add(BuildRuleForTest1(rule));
}
public void LoadRuleForTest2(RuleEngineEntity rule)
{
_ruleSet.Add(BuildRuleForTest2(rule));
}
public List<IRuleDefinition> BuildRuleForTest1(RuleEngineEntity rule)
{
return Test1(rule);
}
public List<IRuleDefinition> BuildRuleForTest2(RuleEngineEntity rule)
{
return Test2(rule);
}
public List<IRuleDefinition> Test1(RuleEngineEntity rule)
{
RuleBuilder builder = new RuleBuilder();
builder.Name("DefaultRules");
try
{
var modelPattern = builder.LeftHandSide().Pattern(typeof(Customer), "CustomerCheck");
var modelParameter = modelPattern.Declaration.ToParameterExpression();
var expres = Expression.Property(modelParameter, rule.FieldName);
var binaryExpression = Expression.GreaterThan(expres, Expression.Constant(rule.Value));
LambdaExpression expressionCondition = Expression.Lambda(binaryExpression,
modelParameter);
modelPattern.Condition(expressionCondition);
Expression<Action<IContext, Customer, RuleEngineEntity>> action =
(ctx, CustomerCheck, rules) => FireActionAsync(ctx, CustomerCheck, rules);
builder.RightHandSide().Action(action);
}
catch (Exception e)
{
// throw new Exception(e.Message);
}
var buildRule = builder.Build();
return new List<IRuleDefinition> { buildRule };
}
public List<IRuleDefinition> Test2(RuleEngineEntity rule)
{
RuleBuilder builder = new RuleBuilder();
builder.Name("DefaultRules");
try
{
var modelPattern = builder.LeftHandSide().Pattern(typeof(RuleEngineRequestModel), "CustomerCheck");
var modelParameter = modelPattern.Declaration.ToParameterExpression();
var customerDataInArray = Expression.Property(modelParameter, nameof(RuleEngineRequestModel.customerData));
var indx = Expression.Parameter(typeof(int), "index");
var customerData = Expression.ArrayIndex(customerDataInArray, indx);
var expres = Expression.Property(customerData, rule.FieldName);
var binaryExpression = Expression.GreaterThan(expres, Expression.Constant(rule.Value));
LambdaExpression expressionCondition = Expression.Lambda(binaryExpression,
modelParameter);
modelPattern.Condition(expressionCondition);
Expression<Action<IContext, Customer>> action =
(ctx, CustomerCheck) => FireActionAsync(ctx, CustomerCheck, null);
builder.RightHandSide().Action(action);
}
catch (Exception e)
{
// throw new Exception(e.Message);
}
var buildRule = builder.Build();
return new List<IRuleDefinition> { buildRule };
}
public void FireActionAsync(IContext ctx, Customer customer, RuleEngineEntity rule=null)
{
Console.WriteLine($"{rule.Name} Triggered");
}
}
Error: variable 'index' of type 'System.Int32' referenced from scope '', but it is not defined
Expected: want to validate rule against array data with dynamic indexer.
At a quick glance, it appears that you are passing in an array of Customers when inserting the facts into the session, but the rule is looking for the RuleEngineRequestModel.
Also, side note - why are you initialising an array as a List, then converting to an array, rather than just initialising as an array? i.e.
var ruleEngineRequestModel = new RuleEngineRequestModel
{
ruleList = rules,
customerData = {
new Customer { Name = "A", Age = 28 },
new Customer { Name = "B", Age = 26 }
}
};
Finally, why are you inserting the rules at the same time as the data? That seems like it would cause more headaches than benefits, especially seeing as your runtime rule builder ignores them entirely.
EDIT: Having had a chance to see the updated code, this confirms my suspicions - if you use Rule 1 for both tests, it should work (rather, it will fire for each individual customer). The reason why Rule 2 never works is because it's expecting a RuleEngineRequestModel, but you are passing the IEnumerable in directly. Either pass in the request model directly and continue to use Rule 2, or scrap Rule 2 entirely.
I have following program that fetches the data from DB and sends it to Main. I am able to iterate through the result in the function but not in Main.
Program is below :
void Main()
{
var data = GetAllCountry();
// foreach( var t in data)
// {
// Console.WriteLine("{0}", t.country.ID); //fails here; says country not found
// }
}
// Define other methods and classes here
public IEnumerable GetAllCountry()
{
var countries = COUNTRY.Select(c => new
{
country = new
{
ID = c.ID,
Description = c.DESCRIPTION,
CountryPhoneCode = c.COUNTRY_PHONE_CODE,
Currency = c.CURRENCY.CURRENCY_SYMBOL,
}
});
foreach( var t in countries)
{
Console.WriteLine("{0}", t.country.ID);//works here and I am able to access t.country.ID here...
}
return countries;
}
What wrong with this ? what are required modifications ?
I believe as you are returning IEnumerable rather than IEnumerable<T>, it is not able to get the object type.
If you create a class for Country and the method returns IEnumerable<Country> it would work
public IEnumerable<Country> GetAllCountry()
{
var countries = COUNTRY.Select(c => new
{
country = new Country
{
ID = c.ID,
Description = c.DESCRIPTION,
CountryPhoneCode = c.COUNTRY_PHONE_CODE,
Currency = c.CURRENCY.CURRENCY_SYMBOL,
}
});
foreach( var t in countries)
{
Console.WriteLine("{0}", t.country.ID);//works here and I am able to access t.country.ID here...
}
return countries;
}
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
I've got the following classes:
public class SupplierCategory : IEquatable<SupplierCategory>
{
public string Name { get; set; }
public string Parent { get; set; }
#region IEquatable<SupplierCategory> Members
public bool Equals(SupplierCategory other)
{
return this.Name == other.Name && this.Parent == other.Parent;
}
#endregion
}
public class CategoryPathComparer : IEqualityComparer<List<SupplierCategory>>
{
#region IEqualityComparer<List<SupplierCategory>> Members
public bool Equals(List<SupplierCategory> x, List<SupplierCategory> y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(List<SupplierCategory> obj)
{
return obj.GetHashCode();
}
#endregion
}
And i'm using the following linq query:
CategoryPathComparer comparer = new CategoryPathComparer();
List<List<SupplierCategory>> categoryPaths = (from i in infoList
select
new List<SupplierCategory>() {
new SupplierCategory() { Name = i[3] },
new SupplierCategory() { Name = i[4], Parent = i[3] },
new SupplierCategory() { Name = i[5], Parent = i[4] }}).Distinct(comparer).ToList();
But the distinct does not do what I want it to do, as the following code demonstrates:
comp.Equals(categoryPaths[0], categoryPaths[1]); //returns True
Am I using this in a wrong way? why are they not compared as I intend them to?
Edit:
To demonstrate the the comparer does work, the following returns true as it should:
List<SupplierCategory> list1 = new List<SupplierCategory>() {
new SupplierCategory() { Name = "Cat1" },
new SupplierCategory() { Name = "Cat2", Parent = "Cat1" },
new SupplierCategory() { Name = "Cat3", Parent = "Cat2" }
};
List<SupplierCategory> list1 = new List<SupplierCategory>() {
new SupplierCategory() { Name = "Cat1" },
new SupplierCategory() { Name = "Cat2", Parent = "Cat1" },
new SupplierCategory() { Name = "Cat3", Parent = "Cat2" }
};
CategoryPathComparer comp = new CategoryPathComparer();
Console.WriteLine(comp.Equals(list1, list2).ToString());
Your problem is that you didn't implement IEqualityComparer correctly.
When you implement IEqualityComparer<T>, you must implement GetHashCode so that any two equal objects have the same hashcode.
Otherwise, you will get incorrect behavior, as you're seeing here.
You should implement GetHashCode as follows: (courtesy of this answer)
public int GetHashCode(List<SupplierCategory> obj) {
int hash = 17;
foreach(var value in obj)
hash = hash * 23 + obj.GetHashCode();
return hash;
}
You also need to override GetHashCode in SupplierCategory to be consistent. For example:
public override int GetHashCode() {
int hash = 17;
hash = hash * 23 + Name.GetHashCode();
hash = hash * 23 + Parent.GetHashCode();
return hash;
}
Finally, although you don't need to, you should probably override Equals in SupplierCategory and make it call the Equals method you implemented for IEquatable.
Actually, this issue is even covered in documentation:
http://msdn.microsoft.com/en-us/library/bb338049.aspx.