I try Automapper and it is very nice but is it possible map two OuterDto source to OuterModel destination with same object InnerDeto like in code? How can I do that dest1.Inner and dest2.Inner after map has same instance? What I know, I think it is not possible. What do you think? Thanks for help me
public class OuterDto
{
public int Value { get; set; }
public InnerDto Inner { get; set; }
}
public class InnerDto
{
public int OtherValue { get; set; }
}
public class OuterModel
{
public int Value { get; set; }
public InnerModel Inner { get; set; }
}
public class InnerModel
{
public int OtherValue { get; set; }
}
public class test
{
public test()
{
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<InnerDto, InnerModel>().ReverseMap();
cfg.CreateMap<OuterDto, OuterModel>().ReverseMap();
});
config.AssertConfigurationIsValid();
InnerDto innerSource = new InnerDto { OtherValue = 15 };
var source1 = new OuterDto
{
Value = 1,
Inner = innerSource
};
var source2 = new OuterDto
{
Value = 2,
Inner = innerSource
};
var mapper = config.CreateMapper();
source1.Inner.OtherValue = 20;
var dest1 = mapper.Map<OuterDto, OuterModel>(source1);
var dest2 = mapper.Map<OuterDto, OuterModel>(source2);
dest1.Inner.OtherValue = 1000;
//Result:
//dest1.Inner.OtherValue = 1000
//dest2.Inner.OtherValue = 20
//Expected Result:
//dest1.Inner.OtherValue = 1000
//dest2.Inner.OtherValue = 1000
}
}
I'm not sure, but try to instanciate OuterModel before calling Map method
//...
var mapper = config.CreateMapper();
source1.Inner.OtherValue = 20;
var dest1 = new OuterModel();
mapper.Map(source1, dest1);
mapper.Map(source2, dest1);
dest1.Inner.OtherValue = 1000;
NOTE: I haven't tested my code, it's just to give food for thought
Related
I am new to using CSVHelper and AutoMapper and am getting the following error when trying to:
public async Task<IActionResult> OnPostAsync()
{
if (BolCsv != null)
{
try
{
using (var reader = new StreamReader(BolCsv.OpenReadStream()))
using (var csvr = new CsvReader(reader, System.Globalization.CultureInfo.CurrentCulture))
{
csvr.Configuration.Delimiter = "\t";
csvr.Configuration.HeaderValidated = null;
csvr.Configuration.MissingFieldFound = null;
var bolDtos = csvr.GetRecords<BOLDto>().ToList();
var bols = _mapper.Map<IEnumerable<BOL>>(bolDtos);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
var bolDtos = csvr.GetRecords<BOLDto>();
Error: No members are mapped for type 'BOLDto'.
BOLDto:
{
public class BOLDto
{
[Name("BOLNumber")]
public int BOLNumber { get; set; }
[Name("ProductID")]
public int ProductID { get; set; }
[Name("ProductDescription")]
public string ProductDescription { get; set; }
etc...
}
}
BOL.cs:
{
public class BOL
{
public int BOLNumber { get; set; }
public int ProductId { get; set; }
public string ProductDescription { get; set; }
etc...
}
}
As I mentioned Im new to ASP.Net Core AutoMapper, and CSVHelper... how do I solve this issue?
It looks like your BOLDto class has properties, but that error message is the same as I would get if the class had fields instead of properties. So you might want to try CsvHelper.Configuration.MemberTypes.Fields. Also that must be an older version of CsvHelper you are using, because that is not how you would need to set up the configuration in the current version. But it should still work for you to add csvr.Configuration.MemberTypes = CsvHelper.Configuration.MemberTypes.Fields.
void Main()
{
var config = new MapperConfiguration(cfg => cfg.CreateMap<BOLDto, BOL>());
var _mapper = config.CreateMapper();
var csvConfig = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = "\t",
HeaderValidated = null,
MissingFieldFound = null,
MemberTypes = CsvHelper.Configuration.MemberTypes.Fields
};
using (var reader = new StringReader("BOLNumber\tProductID\tProductDescription\t\n1\t2\tMy Product"))
using (var csvr = new CsvReader(reader, csvConfig))
{
var bolDtos = csvr.GetRecords<BOLDto>().ToList();
var bols = _mapper.Map<IEnumerable<BOL>>(bolDtos);
}
}
public class BOLDto
{
[Name("BOLNumber")]
public int BOLNumber;
[Name("ProductID")]
public int ProductID;
[Name("ProductDescription")]
public string ProductDescription;
}
public class BOL
{
public int BOLNumber { get; set; }
public int ProductId { get; set; }
public string ProductDescription { get; set; }
}
I have a set of POCO facets with ranges, that I have received from a client. I need to convert these ultimately into an AggregationDictionary. I cannot figure out the syntax for creating a dynamic set of aggregations (possilbly of type RangeAggregationDescriptor) and need help with this.
My POCO objects are below:
public class TypedFacets
{
public string Name { get; set; }
public string Field { get; set; }
public IReadOnlyCollection<Range> RangeValues { get; set; } = new List<Range>();
public int Size { get; set; }
}
public class Range
{
public string Name { get; set; }
public double? From { get; set; }
public double? To { get; set; }
}
The Nest generation looks like below:
var facets = new List<TypedFacets>()
{
new TypedFacets()
{
Name = "potatoRange",
Field = "potatoRange",
RangeValues = new List<Range>()
{
new Range()
{
From = 0,
To = null,
Name = "chips"
},
new Range()
{
From = 1,
To = null,
Name = "crisps"
}
}
}
};
var aggregations = new AggregationContainerDescriptor<Template>();
facets.Where(f => f.RangeValues.Any()).ToList().ForEach(f =>
{
var rad = new RangeAggregationDescriptor<Template>();
f.RangeValues.ToList().ForEach(rangeValue =>
{
rad = rad.Ranges(rs => rs.From(rangeValue.From).To(rangeValue.To).Key(rangeValue.Name));
});
// this line doesn't work and needs to change
aggregations.Range(f.Name, r => r
.Field(f.Field).Ranges(rs => rad.Ranges));
});
return ((IAggregationContainer)aggregations).Aggregations;
I'm not sure how to fix the above. Any help would be appreciated.
I eventually found the solution for this. You can create the dynamic ranges as per below
private Func<AggregationRangeDescriptor, IAggregationRange>[] CreateRangeRanges(TypedFacets rangedAgg)
{
var rangeRanges = new List<Func<AggregationRangeDescriptor, IAggregationRange>>();
rangedAgg.RangeValues.ToList().ForEach(rangeValue =>
{
rangeRanges.Add(rs => rs.From(rangeValue.From).To(rangeValue.To).Key(rangeValue.Name));
});
return rangeRanges.ToArray();
}
And then assing them like below
facets.Where(f => f.RangeValues.Any()).ToList().ForEach(f =>
{
aggregations.Range(f.Name, r => r
.Field(f.Field).Ranges(CreateRangeRanges(f)));
});
I have following class.
public class Unit
{
public string Name { get; set; }
public double cConvertFromSI { get; set; }
}
public class UnitList
{
public Unit m = new Unit() { Name = "meter", cConvertFromSI = 1 };
public Unit mm = new Unit() { Name = "millimeter", cConvertFromSI = 1000 };
public Unit in = new Unit() { Name = "inch", cConvertFromSI = 39.3701 };
}
And I want to get all 'Unit' from 'UnitList'.
// I want to do something like
UnitList MyUnitList = new UnitList();
foreach (Unit Unit in MyUnitList)
{
// do something with each 'Unit'
}
How can I do it?
You can implement the IEnumerable<Unit> interface.
public class UnitList : IEnumerable<Unit>
{
public Unit m = new Unit() { Name = "meter", cConvertFromSI = 1 };
public Unit mm = new Unit() { Name = "millimeter", cConvertFromSI = 1000 };
public Unit in_ = new Unit() { Name = "inch", cConvertFromSI = 39.3701 };
public IEnumerator<Unit> GetEnumerator()
{
yield return m;
yield return mm;
yield return in_;
//...
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
=> GetEnumerator();
}
That way you can iterate through a UnitList instance with foreach.
foreach ( Unit u in new UnitList() )
{
}
However, it would probably be more reasonable to just use a List or Array property instead.
I hope you have a good reason to not just use a List<Unit>, but this would solve getting the properties dynamically.
public class Unit
{
public string Name { get; set; }
public double cConvertFromSI { get; set; }
public override string ToString()
{
return $"{Name} {cConvertFromSI}";
}
}
public class UnitList
{
public Unit m { get; set; } = new Unit() {Name = "meter", cConvertFromSI = 1};
public Unit mm { get; set; } = new Unit() {Name = "millimeter", cConvertFromSI = 1000};
public Unit iN { get; set; } = new Unit() {Name = "inch", cConvertFromSI = 39.3701}; // in is a reserved keyword btw
}
class Program
{
static void Main(string[] args)
{
var unitList = new UnitList();
var propertyInfos = typeof(UnitList).GetProperties().Where(p => p.PropertyType == typeof(Unit));
var units = propertyInfos.Select(propertyInfo => (Unit) propertyInfo.GetValue(unitList)).ToList();
units.ForEach(u => { Console.WriteLine(u.ToString()); });
}
}
Note that I added {get; set;} at the end of UnitList fields to make them properties.
If you want to keep them as fields then you would need to get the units like this
var fields = typeof(UnitList).GetFields().Where(p => p.FieldType == typeof(Unit));
var units = fields.Select(propertyInfo => (Unit) propertyInfo.GetValue(unitList)).ToList();
What Am I doing wrong? In the code below (specifically on foreach), When I have more than one 'itens', just the last one will be on Itens array.
I am using a viewmodel array, as you can see below.
I would appreciate if you could give me a sample.
I created a repository class like this:
public async Task<PrintNFePedidoRoot> PedidoPrint(int idPedido)
{
using var db = new KeplerContext(_optionsBuilder);
var pedido = await db.Pedido.AsNoTracking()
.FirstOrDefaultAsync(m => m.IdPedido == idPedido);
var pedidoItens = await db.PedidoItens
.Where(i => i.IdPedido == idPedido)
.Include(p => p.IdProdutoNavigation)
.AsNoTracking().ToListAsync();
var pedidoPrint = new PrintNFePedidoRoot()
{
IdPedido = idPedido,
TotalValor = pedido.Val,
Desconto = 0,
Taxa = pedido.Tax,
TotalPagar = pedido.Tot,
};
var Itens = Array.Empty<PrintNFePedidoItens>();
foreach (PedidoItens i in pedidoItens)
{
if (i.IdProdutoMeia != null)
{
var produto = db.Produto
.AsNoTracking()
.First(p => p.IdProduto == i.IdProdutoMeia);
i.IdProdutoNavigation.Nome = i.IdProdutoNavigation.Nome + "/" + produto.Nome.Replace("Exp", "").Trim();
}
Itens = new[]
{
new PrintNFePedidoItens
{
IdProduto = i.IdProduto,
Nome = i.IdProdutoNavigation.Nome,
Valor = i.IdProdutoNavigation.Preco
}
};
}
pedidoPrint.Itens = Itens;
return pedidoPrint;
}
I created a viewModel class like these:
public class PrintNFePedidoRoot
{
[JsonProperty("id_pedido")]
public int IdPedido { get; set; }
[JsonProperty("total_valor")]
public int TotalValor { get; set; }
[JsonProperty("desconto")]
public int Desconto { get; set; }
[JsonProperty("taxa")]
public int Taxa { get; set; }
[JsonProperty("total_pagar")]
public int TotalPagar { get; set; }
[JsonProperty("pedido_itens")]
public PrintNFePedidoItens[] Itens { get; set; }
}
public class PrintNFePedidoItens
{
[JsonProperty("id_produto")]
public int IdProduto { get; set; }
[JsonProperty("nome")]
public string Nome { get; set; }
[JsonProperty("valor")]
public decimal Valor { get; set; }
}
Looks like you are assigning a new array with just one element to "Itens" instead to add values to the array.
Instead of this
Itens = new[]
{
new PrintNFePedidoItens
{
IdProduto = i.IdProduto,
Nome = i.IdProdutoNavigation.Nome,
Valor = i.IdProdutoNavigation.Preco
}
};
Try
Itens.Add(new PrintNFePedidoItens
{
IdProduto = i.IdProduto,
Nome = i.IdProdutoNavigation.Nome,
Valor = i.IdProdutoNavigation.Preco
});
Just be sure you are initializing correctly "Itens".
This way is working.
Instead of this:
var Itens = Array.Empty<PrintNFePedidoItens>();
I have changed to this:
var Itens = new List<PrintNFePedidoItens>();
Instead of this:
public class PrintNFePedidoRoot
{
...
[JsonProperty("pedido_itens")]
public PrintNFePedidoItens[] Itens { get; set; }
}
I have changed to this:
public class PrintNFePedidoRoot
{
...
[JsonProperty("pedido_itens")]
public List<PrintNFePedidoItens> Itens { get; set; }
}
So, I could used this:
Itens.Add(new PrintNFePedidoItens
{
IdProduto = i.IdProduto,
Nome = i.IdProdutoNavigation.Nome,
Valor = i.IdProdutoNavigation.Preco,
Qntd = i.Quantidade
});
I've been working on using reflection but its very new to me still. So the line below works. It returns a list of DataBlockOne
var endResult =(List<DataBlockOne>)allData.GetType()
.GetProperty("One")
.GetValue(allData);
But I don't know myType until run time. So my thoughts were the below code to get the type from the object returned and cast that type as a list of DataBlockOne.
List<DataBlockOne> one = new List<DataBlockOne>();
one.Add(new DataBlockOne { id = 1 });
List<DataBlockTwo> two = new List<DataBlockTwo>();
two.Add(new DataBlockTwo { id = 2 });
AllData allData = new AllData
{
One = one,
Two = two
};
var result = allData.GetType().GetProperty("One").GetValue(allData);
Type thisType = result.GetType().GetGenericArguments().Single();
Note I don't know the list type below. I just used DataBlockOne as an example
var endResult =(List<DataBlockOne>)allData.GetType() // this could be List<DataBlockTwo> as well as List<DataBlockOne>
.GetProperty("One")
.GetValue(allData);
I need to cast so I can search the list later (this will error if you don't cast the returned object)
if (endResult.Count > 0)
{
var search = endResult.Where(whereExpression);
}
I'm confusing the class Type and the type used in list. Can someone point me in the right direction to get a type at run time and set that as my type for a list?
Class definition:
public class AllData
{
public List<DataBlockOne> One { get; set; }
public List<DataBlockTwo> Two { get; set; }
}
public class DataBlockOne
{
public int id { get; set; }
}
public class DataBlockTwo
{
public int id { get; set; }
}
You might need something like this:
var endResult = Convert.ChangeType(allData.GetType().GetProperty("One").GetValue(allData), allData.GetType());
Just guessing, didn't work in C# since 2013, please don't shoot :)
You probably want something like this:
static void Main(string[] args)
{
var one = new List<DataBlockBase>();
one.Add(new DataBlockOne { Id = 1, CustomPropertyDataBlockOne = 314 });
var two = new List<DataBlockBase>();
two.Add(new DataBlockTwo { Id = 2, CustomPropertyDatablockTwo = long.MaxValue });
AllData allData = new AllData
{
One = one,
Two = two
};
#region Access Base Class Properties
var result = (DataBlockBase)allData.GetType().GetProperty("One").GetValue(allData);
var oneId = result.Id;
#endregion
#region Switch Into Custom Class Properties
if (result is DataBlockTwo)
{
var thisId = result.Id;
var thisCustomPropertyTwo = ((DataBlockTwo)result).CustomPropertyDatablockTwo;
}
if (result is DataBlockOne)
{
var thisId = result.Id;
var thisCustomPropertyOne = ((DataBlockOne)result).CustomPropertyDataBlockOne;
}
#endregion
Console.Read();
}
public class AllData
{
public List<DataBlockBase> One { get; set; }
public List<DataBlockBase> Two { get; set; }
}
public class DataBlockOne : DataBlockBase
{
public int CustomPropertyDataBlockOne { get; set; }
}
public class DataBlockTwo : DataBlockBase
{
public long CustomPropertyDatablockTwo { get; set; }
}
public abstract class DataBlockBase
{
public int Id { get; set; }
}