How to use AutoMapper without Entity Framework? - c#

I am learning how to use AutoMapper. First thing first, I don't use Entity Framework to read my data.
Hence, in my case I have to do manual mapping for each of the properties of my response model.
Below code may help you get more insight of this:
Response model:
public class TotalLossResults
{
public string N_CLAIM_NUMBER { get; set; }
public string N_CLAIM_ID { get; set; }
}
MapperClass:
public class TLResultsMapper : Profile
{
private TotalLossResults tlResultsObj = new TotalLossResults();
public TLResultsMapper()
{
IMappingExpression<DataRow, TotalLossResults> mappingExpression = CreateMap<DataRow, TotalLossResults>();
foreach (var prop in tlResultsObj.GetType().GetProperties())
{
mappingExpression.ForMember(prop.Name, y => y.MapFrom(s => s[prop.Name]));
}
}
}
Note: in the mapper class I used for each to get rid of the mappingExpression.ForMember statement for each property. But this works only when the property name is the same as of the column name (entity name for example) of the result which I get from the database.
I am looking out for some option where I can take similar approach to map the data values to my response model properties when the property's names are not matching with the column names.
I tried doing something like this:
I created another class which has the properties with different names:
public class TLResultsDifferentNames
{
public string N_CLAIM_NUMBER { get; set; }
public string N_CLAIM_ID { get; set; }
}
and a mapper implementation like this:
private TLResultsDifferentNames tlResultsObj = new TLResultsDifferentNames ();
private TotalLossResults tlResultsColObj = new TotalLossResults ();*
for (int i = 0, j = 0; i<tlResultsObj.GetType().GetProperties().Length - 1 && j<tlResultsColObj.GetType().GetProperties().Length - 1; i++, j++)
{
mappingExpression.ForMember(tlResultsObj.GetType().GetProperties()[i].Name, y => y.MapFrom(s => s[tlResultsColObj.GetType().GetProperties()[j].Name]));
}
But this doesn't work. It binds the last column values to all the model properties.
Any help/suggestion to achieve the mapping without using the manual way of mapping would be very helpful.

I could find something really interesting in Auto Mapper today. Which is Attribute Mapping and using that i need not to worry about any sort of manual/dynamical mapping for my models.
Below is the code which works perfectly now for all the properties:
Ex1: here all the properties' names are same
[AutoMap(typeof(object))] //this takes our Source class name
public class TotalLossResults
{
public string N_CLAIM_NUMBER { get; set; }
public string N_CLAIM_ID { get; set; }
}
Ex2: here we got different properties
[AutoMap(typeof(TotalLossResults))] //this takes our Source class name
public class TLResultsDifferentNames
{
[SourceMember(nameof(TotalLossResults.N_CLAIM_NUMBER))]
public string claimNumberOfJack { get; set; }
public string claimIDofJack { get; set; }
}
For mapping configuration we gonna use the below code:
var config1 = new MapperConfiguration(cfg =>
cfg.AddMaps(typeof(TotalLossResults)));
var mapper = new Mapper(config1);
var response = mapper.Map<TotalLossResults>(sourceObject);
Note: Its better to have the configs created in App Start.

Related

How can I enforce camelCasing in cosmos/EF when my .NET classes are named with PascalCasing?

I am encountering this error when trying to query data from cosmos:
The partition key for entity type 'DataModel' is set to
'partitionKey', but there is no property with that name.
Here is what my DataModel class looks like:
public class DataModel
{
/* Properties - Serializable */
public string Id { get; set; } = string.Empty;
public int? Index { get; set; } = null;
public string? PartitionKey { get; set; } = null;
public string Name { get; set; } = string.Empty;
public bool Locked { get; set; } = false;
public string? LockedUser { get; set; } = null;
public List<Record> Records { get; set; } = new List<Record>();
public List<Field> Fields { get; set; } = new List<Field>();
/* Properties - Not-Serializable */
[JsonIgnore]
[NotMapped]
public Record? RootRecord = null;
[JsonIgnore]
[NotMapped]
public BehaviorSubject<Record?> RootRecordBH = new BehaviorSubject<Record?>(null);
}
Here is how I am implementing OnModelCreating:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<DataModel>().Property(x => x.Id).ToJsonProperty("id");
modelBuilder.Entity<DataModel>().Property(x => x.Index).ToJsonProperty("index");
modelBuilder.Entity<DataModel>().Property(x => x.PartitionKey).ToJsonProperty("partitionKey");
modelBuilder.Entity<DataModel>().Property(x => x.Name).ToJsonProperty("name");
modelBuilder.Entity<DataModel>().Property(x => x.Locked).ToJsonProperty("locked");
modelBuilder.Entity<DataModel>().Property(x => x.LockedUser).ToJsonProperty("lockedUser");
modelBuilder.Entity<DataModel>().Property(x => x.Records).ToJsonProperty("records");
modelBuilder.Entity<DataModel>().Property(x => x.Fields).ToJsonProperty("fields");
modelBuilder.Entity<DataModel>()
.ToContainer("DataModels")
.HasPartitionKey("partitionKey");
}
My understanding is that you can control property names in cosmos by using the .ToJsonProperty in OnModelBuilding() when using EF/EFCore, although this appears to be doing nothing.
I'm not quite sure what I'm missing here.
I tried to execute the code shared in question and able to save data in CosmosDB with camelCasing format. My data model class has PascalCasing.
The only change done in the code is .HasPartitionKey(x=>x.PartitionKey); As mentioned by Jeremy Lakeman in comment HasPartitionKey method accepts model property name not the json name.
Also to execute the code I have used below nuget packages and their respective version.
Microsoft.EntityFrameworkCore.Cosmos 7.0.1
Microsoft.EntityFrameworkCore 7.0.1
References:-
EF modeling
Azure Cosmos DB Provider - EF Core | Microsoft Learn
HasPartitionKey Method

Auto mapper - how can I automatically create maps and avoid explicit map creation? (10.0+)

I am looking some utility using which I can avoid writing extra line of code.
For Example
config.CreateMap<ModelClass, DTOClass>();
Though I dont have any difference between ModelClass and DTOClass still I need to create map or automapper can do it by itself?
As per the comments, seemingly the Automapper team elected to remove the CreateMissingTypeMaps option in the 9.0 upgrade. Speculatively, this was because automatic map creation could lead to unexpected mappings and awkward runtime bugs, and also, it is preferable to define all maps at bootstrap time and compile them, rather than have them compiled lazily on the fly during first mapping.
However, if you use a consistent naming scheme between your Poco class layers, you can quite easily replicate the automapping capability, at bootstrap time, for all classes with the same names in the two poco layers. For example, if you're convention is:
namespace Models
{
public class MyModel
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Amount { get; set; }
public DateTime Date { get; set; }
}
}
namespace Dtos
{
public class MyDto
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Amount { get; set; }
public DateTime Date { get; set; }
}
}
You can then reflect out all the classes meeting your naming convention in each of the layers, and explicitly create a mapping between the matching classes:
const string modelSuffix = "Model";
const string dtoSuffix = "Dto";
var mapperConfiguration = new MapperConfiguration(cfg =>
{
// You may need to repeat this if you have Pocos spread across multiple assemblies
var modelTypes = typeof(MyModel).Assembly
.GetTypes()
.Where(type => type.Namespace == "Models" && type.Name.EndsWith(modelSuffix))
.ToDictionary(t => StripSuffix(t.Name, modelSuffix));
var dtoTypes = typeof(MyDto).Assembly
.GetTypes()
.Where(type => type.Namespace == "Dtos"
&& type.Name.EndsWith(dtoSuffix));
foreach (var dtoType in dtoTypes)
{
if (modelTypes.TryGetValue(StripSuffix(dtoType.Name, dtoSuffix), out var modelType))
{
// I've created forward and reverse mappings ... remove as necessary
cfg.CreateMap(dtoType, modelType);
cfg.CreateMap(modelType, dtoType);
}
}
});
var mapper = mapperConfiguration.CreateMapper();
StripSuffix is simply:
public static string StripSuffix(string someString, string someSuffix)
{
if (someString.EndsWith(someSuffix))
return someString.Substring(0, someString.Length - someSuffix.Length);
else
return someString;
}
Which you can now test as applicable:
var model = new MyModel
{
Id = 1,
Name = "Foo",
Amount = 1.23m,
Date = DateTime.UtcNow
};
var dto = mapper.Map<MyDto>(model);
var backTomodel = mapper.Map<MyModel>(dto);

Using .Select to get single item from sub list, then assigning individual properties of that item to view model

I have pretty much solved this problem but I am wondering whether there is a more efficient way of doing this using Entity framework / SQL.
Essentially, what i am doing is performing a subquery to get a SINGLE item on a list of objects that are connected to a parent entity. I then want to extract only a few columns from that single entity.
The first way, which doesn't work but shows my possible thought process was to put each object into a temporary variable and then create the view:
_context.IcoInfos.Select((i) =>
{
var reward = i.SocialRewards.OrderByDescending(s => s.EndDate).FirstOrDefault();
return new IcoInfoRewardCountViewModel()
{
CampaignName = i.Name,
CurParticipants = reward.CurParticipants,
Title = reward.CustomTitle,
IsLive = reward.IsLive
};
});
The second way, which works, I am creating a temporary model which stores the single database row of the sublist result...
_context.IcoInfos.Select((i) => new
{
Reward = i.SocialRewards.OrderByDescending(s => s.EndDate).FirstOrDefault(),
IcoName = i.Name
}).Select(t => new IcoInfoRewardCountViewModel()
{
CampaignName = t.IcoName,
CurParticipants = t.Reward.CurParticipants,
Title = t.Reward.CustomTitle,
IsLive = t.Reward.IsLive
}).ToList();
My question is, is this second way the only/best way to achieve this?
Your second approach is ok but for bigger application will cause you trouble if application growth larger and you have a lot information to store in the model.
So I think you can use automapper to make your code more clean.
Example
To use autoampper I need to define a model class and DTO class that share some same properties.
public class Comment
{
public string Content { get; set; }
public virtual Comment ParentComment { get; set; }
public virtual Post Post { get; set; }
public virtual User? User { get; set; }
public CommentStatus CommentStatus { get; set; }
}
public class CommentDto
{
public int Id { get; set; }
public Guid UniqeId { get; set; }
public string Content { get; set; }
public Comment ParentComment { get; set; }
public CommentStatus CommentStatus { get; set; }
public DateTime DateCreated { get; set; }
}
I also need to define the profile class to register mapping
public class CommentProfile : Profile
{
public CommentProfile()
{
CreateMap<Comment, CommentDto>(MemberList.None).ReverseMap();
}
}
Then I will need to register into DI container in startup.cs
services.AddAutoMapper();
Then I can use like this
var comments = await _unitOfWork.Repository<Comment>().Query()
.Include(x => x.User)
.Include(c => c.Post)
.Select(x => new CommentViewModel
{
Comment = _mapper.Map<Comment, CommentDto>(x),
})
.ToListAsync();
It will make the code more clear and I dont have to do manual mapping

Best way to add base class values to derived class?

I have a reference file (dll) containing a class, which i use as my base class:
public class Group
{
public Group();
public int Id { get; set; }
public int League_Id { get; set; }
public string Name { get; set; }
public string Nationality { get; set; }
}
(Please note that the Group class contains about 30 entities, i have only displayed some)
Since i use Entity Framework the ID needs to be unique. i get this data from a API call and noticed that the ID is not unique, i have added my own class.
public class modGroup : APICall.Group
{
public modGroup()
{
modID = 0;
}
[Key]
public int modID { get; set; }
}
This setup works, EF is creating the database.
What i'd like to do is get the data from the API (Which is structured as the Group class), create a new modGroup() and set all data from the API call, without referencing each individual object.
What i would like to do is transfer data without setting each individual entity.
List<APICall.Group> groupData= _ApiRequester.GetGroup();
using (var a = new databaseModel())
{
foreach (APICall.Group x in groupData)
{
var modGroup = new Models.modGroup();
modGroup.modID = 0;
// fill modgroup with rest of variables without doing:
// modGroup.League_iD = a.League_iD;
// modGroup.Name = a.Name;
// etc
}
}
I would use Automapper to map the two classes together in one call. This type of situation is what it was designed for. All you would need to do is create a mapping configuration and then map the two classes. It's a very simple but powerful tool. e.g. Something like this:
var config = new MapperConfiguration(cfg => cfg.CreateMap<APICall.Group, modGroup>()
.ForMember(dest => dest.modID, s=>s.MapFrom(s=>s.Id));
// etc create more mappings here
);
var mapper = config.CreateMapper();
List<modGroup> modGroupList = mapper.Map<List<modGroup>>(groupData);

Add additional data to model (downcasting?)

I got the following entity model which I use in Entity Framework:
public class User {
public int Id { get; set; }
public string Name { get; set; }
public string EMail { get; set; }
}
Now I'm trying to display to user on a view (MVVM in WPF, MVC in ASP.NET...), but along with other information that isn't available inside the database, but can be fetched at runtime from a service.
For this, I created a derived model class:
public class UserDetail : User {
public bool IsOnline { get; set; }
}
And now some gibberish code that describes what I want to achieve:
var users = _myContext.Users
.ToList()
.Select(x => new UserDetail() {
IsOnline = _myUserService.IsOnline(x.Id)
} = (UserDetail)x); // downcast x (User) to the new UserDetail instance
return View["MyView", users];
Now, downcasting doesn't work that way in C#.. do I have any other options to achieve what I want?
You can add a constructor by copy to UserDetail.
public class UserDetail : User
{
public UserDetail(User x)
{
this.Id = x.Id;
this.Name = x.Name;
this.EMail = x.EMail;
}
public bool IsOnline { get; set; }
}
(that kind of constructor can be generated by T4 if you have many class with this behaviour)
then, change your linq and use that constructor :
var users = _myContext.Users
.ToList()
.Select(x => new UserDetail(x) {
IsOnline = _myUserService.IsOnline(x.Id)
});
return View["MyView", users];
No, you have to copy the properties one by one, or write some code which will do it for you.
Create a separate view model. You shouldn't expand your entity models to accommodate for properties required by your view. Then you can either copy properties one by one as zahorak suggested or use a library specificaly made for this task like AutoMapper.

Categories

Resources