I am using AutoMapper to map DTOs to entities. Also, my WCF services are being consumed by SAP.
The issue is that SAP sends me empty strings instead of nulls (that is, "" instead of null).
So I basically need to go through every field of the DTO I am receiving, and replace empty strings by nulls. Is there an easy way to accomplish this with AutoMapper?
Consider value transform construction for mapper profile
CreateMap<Source, Destination>()
.AddTransform<string>(s => string.IsNullOrEmpty(s) ? null : s);
This construction will transform all 'string' type members and if they null or empty replace with null
Depends on what you are after - if there are string fields for which you would like to preserve the empty string and not convert to null, or you want to threat all of them the same. The provided solution is if you need to threat them all the same. If you want to specify individual properties for which the empty to null conversion should happen, use ForMemeber() instead of ForAllMembers.
Convert all solution:
namespace Stackoverflow
{
using AutoMapper;
using SharpTestsEx;
using NUnit.Framework;
[TestFixture]
public class MapperTest
{
public class Dto
{
public int Int { get; set; }
public string StrEmpty { get; set; }
public string StrNull { get; set; }
public string StrAny { get; set; }
}
public class Model
{
public int Int { get; set; }
public string StrEmpty { get; set; }
public string StrNull { get; set; }
public string StrAny { get; set; }
}
[Test]
public void MapWithNulls()
{
var dto = new Dto
{
Int = 100,
StrNull = null,
StrEmpty = string.Empty,
StrAny = "any"
};
Mapper.CreateMap<Dto, Model>()
.ForAllMembers(m => m.Condition(ctx =>
ctx.SourceType != typeof (string)
|| ctx.SourceValue != string.Empty));
var model = Mapper.Map<Dto, Model>(dto);
model.Satisfy(m =>
m.Int == dto.Int
&& m.StrNull == null
&& m.StrEmpty == null
&& m.StrAny == dto.StrAny);
}
}
}
You can just define string mapping like this:
cfg.CreateMap<string, string>()
.ConvertUsing(s => string.IsNullOrWhiteSpace(s) ? null : s);
You can be specific with the properties too.
cfg.CreateMap<Source, Dest>()()
.ForMember(destination => destination.Value, opt => opt.NullSubstitute(string.Empty)));
Remember if you are using ReverseMap() place it in the very last something like following,
cfg.CreateMap<Source, Dest>()()
.ForMember(destination => destination.Value, opt => opt.NullSubstitute(string.Empty)))
.ReverseMap();;
Related
I want to make PUT request in ASP .NET CORE that user can make to update his note.
It's my UpdateNoteDTO:
public class UpdateNoteDto
{
[MaxLength(50)]
public string NoteTitle { get; set; }
[MaxLength(500)]
public string NoteBody { get; set; }
public int Color { get; set; }
}
and it's my Update method:
public void Update(UpdateNoteDto dto, int noteID)
{
var note = _dbContext
.Notes
.FirstOrDefault(r => r.Id == noteID);
if (note is null)
{
throw new NotFoundException("Note not found");
}
note.NoteTitle = dto.NoteTitle == string.Empty
|| dto.NoteTitle is null
? note.NoteTitle : dto.NoteTitle;
note.NoteBody = dto.NoteBody == string.Empty
|| dto.NoteBody is null
? note.NoteBody : dto.NoteBody;
note.Color = dto.Color == 1 ? note.Color : dto.Color;
_dbContext.SaveChanges();
}
I want to make request that user can make to change single field without need for declare all of them. This code what i wrote is working but i bet there is better solution for this :D
The way you are doing it looks fine but I would suggest two changes,
If you plan to use the endpoint for updating one or some values it's better to change request type to PATCH, the HTTP PATCH request method applies partial modifications to a resource whereas HTTP PUT changes whole resource.
To easily add conditions for mapping DTOs to entity models I would rather use Automapper which allows user to declare mapping conditions in a single spot and use IMapper interface to map the models.
For example if you have two classes:
public class UpdateNoteDto
{
[MaxLength(50)]
public string NoteTitle { get; set; }
[MaxLength(500)]
public string NoteBody { get; set; }
public int Color { get; set; }
}
public class Note
{
public int Id { get; set; }
public string NoteTitle { get; set; }
public string NoteBody { get; set; }
public int Color { get; set; }
}
you can map them by creating a mapping profile with conditions instead of using if, switch or any other way of comparing whether to map or not
mapping Profile could look like this:
public class NoteProfile: Profile
{
public NoteProfile()
{
CreateMap<UpdateNoteDto, Note>()
.ForMember(dest => dest.NoteTitle, opt => opt.Condition(src => !string.IsNullOrEmpty(src.NoteTitle)))
.ForMember(dest => dest.NoteBody, opt => opt.Condition(src => !string.IsNullOrEmpty(src.NoteBody)))
.ForMember(dest => dest.Color, opt => opt.Condition(src => src.Color != default))
.ForMember(dest => dest.Id, opt => opt.Ignore());
}
}
and update the Update(UpdateNoteDto dto, int noteID) function correspondingly:
public void Update(UpdateNoteDto dto, int noteID)
{
var noteToUpdate = _dbContext
.Notes
.FirstOrDefault(r => r.Id == noteID);
if (note is null)
{
throw new NotFoundException("Note not found");
}
var note = _mapper.Map(dto, note);
_dbContext.Update(note);
_dbContext.SaveChanges();
}
client should handled this issue
Or
if you want update an entity, it is better to use PATCH
If the client wants the previous data not to be edited
It should send you the previous data
Maybe, for example, the user wants to field the "NoteBody" value as empty In which case it cannot do this
Problem Setup
I'm attempting to implement validation rules into my app using the System.Linq.Dynamic.Core package, but I'm running into weird behavior. My idea is to have the ability to create and manage these validation rules from within the app itself. So, I have a ValidationRule object in my database defined as:
public class ValidationRule {
public string Description { get; set; }
public string ErrorMessage { get; set; }
public short Id { get; set; }
public bool IsActive { get; set; }
public string Name { get; set; }
public string NewExpression { get; set; }
public string OldExpression { get; set; }
}
The NewExpression and OldExpression properties contain the expressions I want to pass to the dynamic version of Any(). The only important parts of the ValidationRule object are the Name, ErrorMessage, NewExpression, and OldExpression, so I project them into a DTO:
public class ValidationRuleDto {
public string ErrorMessage { get; set; }
public string Name { get; set; }
public string NewExpression { get; set; }
public string OldExpression { get; set; }
}
This DTO is then passed to a ValidationRuleHandler to evaluate an old and a new instance of the object being validated with the expressions from the DTO. The ValidationRuleHandler looks like this:
public static class ValidationRuleHandler {
private static readonly ParsingConfig _parsingConfig = new() {
AreContextKeywordsEnabled = false
};
public static ICollection<string> Validate(
dynamic oldObject,
dynamic newObject,
IEnumerable<ValidationRuleDto> rules) => rules.Select(
_ => {
var oldResult = new[] {
oldObject
}.AsQueryable().Any(_parsingConfig, _.OldExpression);
var newResult = new[] {
newObject
}.AsQueryable().Any(_parsingConfig, _.NewExpression);
Debug.WriteLine($"Old => {oldObject.StageText} => {_.OldExpression} => {oldResult}");
Debug.WriteLine($"New => {newObject.StageText} => {_.NewExpression} => {newResult}");
return !(oldResult && newResult)
? null
: $"[{_.Name}] {_.ErrorMessage}";
}).Where(
_ => _ != null).ToHashSet();
}
For the old and new objects being passed to Validate() I project the object into an old snapshot (from the database as it is) and new snapshot (from the database but with the changes applied) DTOs. In the Validate() I add each of the objects to a single item collection and convert them to an IQueryable so I can use the dynamic Any(). My thought process here is that the expressions for the rule just need to evaluate to a true/false result, and since Any() returns true/false if the expression's conditions passed, it seemed the most appropriate way to go.
Problem
The problem that I'm running into is that the results I expect are not happening when running the app. For reference, the app is an ASP.NET MVC 5 (5.2.9) app targetting .NET Framework 4.8. However, when using LINQPad (5.46.00) to test the ValidationRuleHandler the results are correct. For example here's the output of the Debug statements from the app when it's processing three validation rules that apply to the user:
Old => Not Sold => (StageText != "Closed") => True
New => Closed => (StageText == "Closed") => False WRONG
Old => Not Sold => (StageText != "Not Sold") => True WRONG
New => Closed => (StageText == "Not Sold") and (ReasonNotSoldText == null) => False
Old => Not Sold => (StageText != "Ready to Invoice") => True
New => Closed => (StageText == "Ready to Invoice") and (PricingMethodText == null) => False
And here's the LINQPad result for the exact same validation rules and snapshot objects (exact same values):
Old => Not Sold => (StageText != "Closed") => True
New => Closed => (StageText == "Closed") => True
Old => Not Sold => (StageText != "Not Sold") => False
New => Closed => (StageText == "Not Sold") and (ReasonNotSoldText == null) => False
Old => Not Sold => (StageText != "Ready to Invoice") => True
New => Closed => (StageText == "Ready to Invoice") and (PricingMethodText == null) => False
As you can see, when ValidationRuleHandler is called from LINQPad it evaluates the expressions correctly, but when processed from the app, it's wrong.
I can't figure out why it's behaving this way. Thinking through it, I can't see a problem, and LINQPad behaves as expected, but the app is not and I don't know what else to do. If anyone has any suggestions, I'd appreciate them. I'm also open to alternative suggestions to accomplish the same goal if anyone has had to deal with something similar before.
When creating a .NET Full Framework 4.8 console app, with the following example code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
namespace ConsoleApp48DynamicLinq
{
public class ValidationRule
{
public string Description { get; set; }
public string ErrorMessage { get; set; }
public short Id { get; set; }
public bool IsActive { get; set; }
public string Name { get; set; }
public string NewExpression { get; set; }
public string OldExpression { get; set; }
}
public class ValidationRuleDto
{
public string ErrorMessage { get; set; }
public string Name { get; set; }
public string NewExpression { get; set; }
public string OldExpression { get; set; }
}
public class Test
{
public string StageText { get; set; }
}
internal class Program
{
static void Main(string[] args)
{
var old = new Test
{
StageText = "Not Sold"
};
var #new = new Test
{
StageText = "Closed"
};
var validation = new ValidationRuleDto
{
OldExpression = "(StageText != \"Closed\")",
NewExpression = "(StageText == \"Closed\")"
};
var validations = new[] { validation };
ValidationRuleHandler.Validate(old, #new, validations);
}
public static class ValidationRuleHandler
{
private static readonly ParsingConfig ParsingConfig = new ParsingConfig
{
AreContextKeywordsEnabled = false
};
public static ICollection<string> Validate(
dynamic oldObject,
dynamic newObject,
IEnumerable<ValidationRuleDto> rules) => rules.Select(
_ =>
{
var oldResult = new[]
{
oldObject
}.AsQueryable().Any(ParsingConfig, _.OldExpression);
var newResult = new[]
{
newObject
}.AsQueryable().Any(ParsingConfig, _.NewExpression);
Console.WriteLine($"Old => {oldObject.StageText} => {_.OldExpression} => {oldResult}");
Console.WriteLine($"New => {newObject.StageText} => {_.NewExpression} => {newResult}");
return !(oldResult && newResult) ? null : $"[{_.Name}] {_.ErrorMessage}";
}).Where(_ => _ != null).ToHashSet();
}
}
}
It seems to work fine:
So, after many hours of trying different solutions to see where exactly it was failing, nothing worked, until I changed the expression to use Equals(). Instantly worked after that, and I'm not sure why this: (StageText.Equals("Closed")); works and this: (StageText == "Closed"); doesn't.
I'm still baffled why the original way worked when I tested it in LINQPad but not in the app. #Stef also confirmed it works, so I'm confused. Maybe it has something to do with the snapshot values being pulled from the database, or the validation rule expressions also being pulled from the database?
Anyway, it works with Equals() so I'm rolling with it.
Say I have a class, I want to select multiple objects of it but create one unified object in the end. This is because of the requirement for the collection properties of the object to be combined.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.EntityFrameworkCore.Internal;
using Nozomi.Base.Core;
namespace Nozomi.Data.Models.Currency
{
public class Currency : BaseEntityModel
{
public Currency(ICollection<Currency> currencies)
{
if (currencies.Any())
{
var firstCurr = currencies.FirstOrDefault();
if (firstCurr != null)
{
// Doesn't matter...
Id = firstCurr.Id;
CurrencyTypeId = firstCurr.Id;
CurrencyType = firstCurr.CurrencyType;
Abbrv = firstCurr.Abbrv;
Name = firstCurr.Name;
CurrencySourceId = firstCurr.CurrencySourceId;
CurrencySource = firstCurr.CurrencySource;
WalletTypeId = firstCurr.WalletTypeId;
PartialCurrencyPairs = currencies
.SelectMany(c => c.PartialCurrencyPairs)
.DefaultIfEmpty()
.ToList();
}
}
}
[Key]
public long Id { get; set; }
public long CurrencyTypeId { get; set; }
public CurrencyType CurrencyType { get; set; }
public string Abbrv { get; set; } // USD? MYR? IND?
public string Name { get; set; }
public long CurrencySourceId { get; set; }
public Source CurrencySource { get; set; }
// This will have a number if it is a crypto pair to peg to proper entities
public long WalletTypeId { get; set; } = 0;
public ICollection<PartialCurrencyPair> PartialCurrencyPairs { get; set; }
public bool IsValid()
{
return !String.IsNullOrEmpty(Abbrv) && !String.IsNullOrEmpty(Name) && CurrencyTypeId > 0 && CurrencySourceId > 0;
}
}
}
Here's what a PartialCurrencyPair is:
namespace Nozomi.Data.Models.Currency
{
/// <summary>
/// Partial currency pair.
/// </summary>
public class PartialCurrencyPair
{
public long CurrencyId { get; set; }
public long CurrencyPairId { get; set; }
public bool IsMain { get; set; } = false;
public CurrencyPair CurrencyPair { get; set; }
public Currency Currency { get; set; }
}
}
So basically, if you want to make EURUSD, you'll have to take two currencies to form a pair. A CurrencyPair is made up of two PartialCurrencyPairs. The reason why we can have many EUR or many USDs is that they come from different sources.
Here's what a CurrencyPair is:
public class CurrencyPair : BaseEntityModel
{
[Key]
public long Id { get; set; }
public CurrencyPairType CurrencyPairType { get; set; }
/// <summary>
/// Which CPC to rely on by default?
/// </summary>
public string DefaultComponent { get; set; }
public long CurrencySourceId { get; set; }
public Source CurrencySource { get; set; }
// =========== RELATIONS ============ //
public ICollection<CurrencyPairRequest> CurrencyPairRequests { get; set; }
public ICollection<WebsocketRequest> WebsocketRequests { get; set; }
public ICollection<PartialCurrencyPair> PartialCurrencyPairs { get; set; }
public bool IsValid()
{
var firstPair = PartialCurrencyPairs.First();
var lastPair = PartialCurrencyPairs.Last();
return (CurrencyPairType > 0) && (!string.IsNullOrEmpty(APIUrl))
&& (!string.IsNullOrEmpty(DefaultComponent))
&& (CurrencySourceId > 0)
&& (PartialCurrencyPairs.Count == 2)
&& (firstPair.CurrencyId != lastPair.CurrencyId)
&& (!firstPair.IsMain == lastPair.IsMain);
}
}
I have an IQueryable to combine into one single currency.
Code with comments (The comments basically tells you what I'm trying to achieve.
var query = _unitOfWork.GetRepository<Currency>()
.GetQueryable()
// Do not track the query
.AsNoTracking()
// Obtain the currency where the abbreviation equals up
.Where(c => c.Abbrv.Equals(abbreviation, StringComparison.InvariantCultureIgnoreCase)
&& c.DeletedAt == null && c.IsEnabled)
// Something here that will join the PartialCurrencyPair collection together and create one single Currency object.
.SingleOrDefault();
How do I come about it? Thank you so much in forward! Here's the
progress I've made so far and it works, but I'm pretty LINQ has a beautiful way to make this better and optimised:
var combinedCurrency = new Currency(_unitOfWork.GetRepository<Currency>()
.GetQueryable()
// Do not track the query
.AsNoTracking()
// Obtain the currency where the abbreviation equals up
.Where(c => c.Abbrv.Equals(abbreviation, StringComparison.InvariantCultureIgnoreCase)
&& c.DeletedAt == null && c.IsEnabled)
.Include(c => c.PartialCurrencyPairs)
.ThenInclude(pcp => pcp.CurrencyPair)
.ThenInclude(cp => cp.CurrencyPairRequests)
.ThenInclude(cpr => cpr.RequestComponents)
.ThenInclude(rc => rc.RequestComponentDatum)
.ThenInclude(rcd => rcd.RcdHistoricItems)
.ToList());
return new DetailedCurrencyResponse
{
Name = combinedCurrency.Name,
Abbreviation = combinedCurrency.Abbrv,
LastUpdated = combinedCurrency.PartialCurrencyPairs
.Select(pcp => pcp.CurrencyPair)
.SelectMany(cp => cp.CurrencyPairRequests)
.SelectMany(cpr => cpr.RequestComponents)
.OrderByDescending(rc => rc.ModifiedAt)
.FirstOrDefault()?
.ModifiedAt ?? DateTime.MinValue,
WeeklyAvgPrice = combinedCurrency.PartialCurrencyPairs
.Select(pcp => pcp.CurrencyPair)
.Where(cp => cp.CurrencyPairRequests
.Any(cpr => cpr.DeletedAt == null && cpr.IsEnabled))
.SelectMany(cp => cp.CurrencyPairRequests)
.Where(cpr => cpr.RequestComponents
.Any(rc => rc.DeletedAt == null && rc.IsEnabled))
.SelectMany(cpr => cpr.RequestComponents
.Where(rc =>
rc.ComponentType.Equals(ComponentType.Ask) ||
rc.ComponentType.Equals(ComponentType.Bid)))
.Select(rc => rc.RequestComponentDatum)
.SelectMany(rcd => rcd.RcdHistoricItems
.Where(rcdhi => rcdhi.CreatedAt >
DateTime.UtcNow.Subtract(TimeSpan.FromDays(7))))
.Select(rcdhi => decimal.Parse(rcdhi.Value))
.DefaultIfEmpty()
.Average(),
DailyVolume = combinedCurrency.PartialCurrencyPairs
.Select(pcp => pcp.CurrencyPair)
.Where(cp => cp.CurrencyPairRequests
.Any(cpr => cpr.DeletedAt == null && cpr.IsEnabled))
.SelectMany(cp => cp.CurrencyPairRequests)
.Where(cpr => cpr.RequestComponents
.Any(rc => rc.DeletedAt == null && rc.IsEnabled))
.SelectMany(cpr => cpr.RequestComponents
.Where(rc => rc.ComponentType.Equals(ComponentType.VOLUME)
&& rc.DeletedAt == null && rc.IsEnabled))
.Select(rc => rc.RequestComponentDatum)
.SelectMany(rcd => rcd.RcdHistoricItems
.Where(rcdhi => rcdhi.CreatedAt >
DateTime.UtcNow.Subtract(TimeSpan.FromHours(24))))
.Select(rcdhi => decimal.Parse(rcdhi.Value))
.DefaultIfEmpty()
.Sum(),
Historical = combinedCurrency.PartialCurrencyPairs
.Select(pcp => pcp.CurrencyPair)
.SelectMany(cp => cp.CurrencyPairRequests)
.SelectMany(cpr => cpr.RequestComponents)
.Where(rc => componentTypes != null
&& componentTypes.Any()
&& componentTypes.Contains(rc.ComponentType)
&& rc.RequestComponentDatum != null
&& rc.RequestComponentDatum.IsEnabled
&& rc.RequestComponentDatum.DeletedAt == null
&& rc.RequestComponentDatum.RcdHistoricItems
.Any(rcdhi => rcdhi.DeletedAt == null &&
rcdhi.IsEnabled))
.ToDictionary(rc => rc.ComponentType,
rc => rc.RequestComponentDatum
.RcdHistoricItems
.Select(rcdhi => new ComponentHistoricalDatum
{
CreatedAt = rcdhi.CreatedAt,
Value = rcdhi.Value
})
.ToList())
};
Here's the end result I want on that single object: A DetailedCurrencyResponse object.
public class DistinctiveCurrencyResponse
{
public string Name { get; set; }
public string Abbreviation { get; set; }
public DateTime LastUpdated { get; set; }
public decimal WeeklyAvgPrice { get; set; }
public decimal DailyVolume { get; set; }
}
A historical datum is basically a kvp, where the Key (ComponentType) is an enum.
public class DetailedCurrencyResponse : DistinctiveCurrencyResponse
{
public Dictionary<ComponentType, List<ComponentHistoricalDatum>> Historical { get; set; }
}
public class ComponentHistoricalDatum
{
public DateTime CreatedAt { get; set; }
public string Value { get; set; }
}
The query you have outlined will attempt to return you a single Currency object, but given you are looking for any with a given abbreviation, if multiple currency objects share an abbreviation, the SingleOrDefault could error due to multiple returns.
It sounds like you want to define a structure to represent the currency pairs. That structure is not a Currency entity, but a different data representation. These are commonly referred to as ViewModels or DTOs. Once you've defined what you want to return, you can use .Select() to populate that from the Currency and applicable abbreviations.
For instance, if I create a CurrencySummaryDto which will have the currency ID, Abbrevation, and a string containing all of the applicable pairs:
public class CurrencySummaryDto
{
public long CurrencyId { get; set; }
public string Abbreviation { get; set; }
public string Pairs { get; set;}
}
... then the query...
var currencySummary = _unitOfWork.GetRepository<Currency>()
.GetQueryable()
.AsNoTracking()
.Where(c => c.Abbrv.Equals(abbreviation, StringComparison.InvariantCultureIgnoreCase)
&& c.DeletedAt == null && c.IsEnabled)
.Select( c => new {
c.Id,
c.Abbrv,
Pairs = c.PartialCurrencyPairs.Select(pc => pc.PairName).ToList() // Get names of pairs, or select another annonymous type for multiple properties you care about...
}).ToList() // Alternatively, when intending for returning lots of data use Skip/Take for paginating or limiting resulting data.
.Select( c => new CurrencySummaryDto
{
CurrencyId = c.Id,
Abbreviation = c.Abbrv,
Pairs = string.Join(", ", c.Pairs)
}).SingleOrDefault();
This is if you want to do something like combine data from the currency pairs into something like a string. If you're happy to leave them as a collection of simplified data, then the extra anonymous type and .ToList() are not required, just select directly into the Dto structure. This example combines the data into a string where string.Join() is not supported in EF expressions so we have to get our data out into objects to hand over to Linq2Object for the final mapping.
Edit: Ok, you're requirement/example just got a whole lot more complicated with the pair structure, but you should be able to leverage this into the query rather than selecting the entire graph of entities by moving the selection of those values up into the main query... However...
Given the complexity of the data relationships, the approach I would recommend using since this would be assumed to be a read-only result, would be to construct a View in the database to flatten these averages and totals, then bind a simplified entity to this view rather than attempting to manage this with EF Linq. I believe it can be done with linq, but it will be quite onerous to look at, and a view-based summary entity would be a lot cleaner while keeping the execution of this logic to be executed in the database.
Below are my classes
public class Student {
public long Id { get; set; }
public long? CollegeId { get; set; }
public StudentPersonal StudentPersonal { get; set; }
}
public class StudentPersonal {
public long? EthnicityId { get; set; }
public bool? GenderId { get; set; } // doesn't exist on UpdateModel, requires PropertyMap.SourceMember null check
}
public class UpdateModel{
public long Id { get; set; }
public long? CollegeId { get; set; }
public long? StudentPersonalEthnicityId { get; set; }
}
Below is the AutoMapper config
Mapper.Initialize(a => {
a.RecognizePrefixes("StudentPersonal");
}
Mapper.CreateMap<UpdateModel, StudentPersonal>()
.ForAllMembers(opt => opt.Condition(src => src.PropertyMap.SourceMember != null && src.SourceValue != null));
Mapper.CreateMap<UpdateModel, Student>()
.ForMember(dest => dest.StudentPersonal, opt => opt.MapFrom(src => Mapper.Map<StudentPersonal>(src)))
.ForAllMembers(opt => opt.Condition(src => src.PropertyMap.SourceMember != null && src.SourceValue != null));
And the sample test case:
var updateModel = new StudentSignupUpdateModel();
updateModel.Id = 123;
updateModel.CollegeId = 456;
updateModel.StudentPersonalEthnicityId = 5;
var existingStudent = new Student();
existingStudent.Id = 123;
existingStudent.CollegeId = 777; // this gets updated
existingStudent.StudentPersonal = new StudentPersonal();
existingStudent.StudentPersonal.EthnicityId = null; // this stays null but shouldn't
Mapper.Map(updateModel, existingStudent);
Assert.AreEqual(777, existingStudent.CollegeId); // passes
Assert.AreEqual(5, existingStudent.StudentPersonal.EthnicityId); // does not pass
Has anyone gotten conditional mapping to work with prefixes? It works ok on the non prefixed object.
The lambda you're passing to opts.Condition is too restrictive:
src => src.PropertyMap.SourceMember != null && src.SourceValue != null
In this property's case, src.PropertyMap is null every time (which you might expect, since there's no single property that maps to the destination nested property from the source object).
If you remove the PropertyMap.SourceMember check, your tests will pass. I'm not sure what impact this will have on the rest of your mapping though.
I use c# 4 asp.net and EF 4. I'm precompiling a query, the result should be a collection of Anonymous Type.
At the moment I use this code.
public static readonly Func<CmsConnectionStringEntityDataModel, string, dynamic>
queryContentsList =
CompiledQuery.Compile<CmsConnectionStringEntityDataModel, string, dynamic>
(
(ctx, TypeContent) => ctx.CmsContents.Where(c => c.TypeContent == TypeContent
& c.IsPublished == true & c.IsDeleted == false)
.Select(cnt => new
{
cnt.Title,
cnt.TitleUrl,
cnt.ContentId,
cnt.TypeContent, cnt.Summary
}
)
.OrderByDescending(c => c.ContentId));
I suspect the RETURN for the FUNCTION Dynamic does not work properly and I get this error
Sequence contains more than one element enter code here.
I suppose I need to return for my function a Collection of Anonymous Types...
Do you have any idea how to do it? What I'm doing wrong? Please post a sample of code thanks!
Update:
public class ConcTypeContents
{
public string Title { get; set; }
public string TitleUrl { get; set; }
public int ContentId { get; set; }
public string TypeContent { get; set; }
public string Summary { get; set; }
}
public static readonly Func<CmsConnectionStringEntityDataModel, string, ConcTypeContents> queryContentsList =
CompiledQuery.Compile<CmsConnectionStringEntityDataModel, string, ConcTypeContents>(
(ctx, TypeContent) => ctx.CmsContents.Where(c => c.TypeContent == TypeContent & c.IsPublished == true & c.IsDeleted == false)
.Select(cnt => new ConcTypeContents { cnt.Title, cnt.TitleUrl, cnt.ContentId, cnt.TypeContent, cnt.Summary }).OrderByDescending(c => c.ContentId));
You should not return an anonymous type from a method. Create a concrete type to hold whatever data is currently held in the anonymous type and return that instead.
...
.Select(cnt =>
new ConcType{
Title = cnt.Title,
TitleUrl = cnt.TitleUrl,
ContentId = cnt.ContentId,
TypeContent = cnt.TypeContent,
Summary = cnt.Summary })
...
where:
class ConcType
{
public string Title {get; set;}
//etc...
}