If I have an object like
class Thing
{
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
public string D { get; set; }
}
Dapper works really well with:
return conn.QuerySingle<Thing>(
#"SELECT A, B, C, D
FROM [Thing] WHERE Id = #id", new { id });
But what if my class structure is
class Thing
{
public string A { get; set; }
public string B { get; set; }
public IEnumerable<Stuff> { get; set; }
}
class Stuff
{
public string C { get; set; }
public string D { get; set; }
}
How can I get Dapper to map to that structure, assuming the Enumerable will always contain 1 item in the Enumerable so in reality I am just representing the same data in a different way.
The Dapper tutorials I've seen are quite complex and contain subqueries, but ideally my query shouldn't change because it already returns all the data that should map to the new structure.
Any ideas?
You have to create your own Custom Type Mapper. I've written an article and some samples here:
https://medium.com/dapper-net/custom-type-handling-4b447b97c620
Related
Am trying to Map an Anonymous object in auto mapper but am not getting how to do that. Please find my requirement below
Am getting some data from joining 2 tables with only one common column(Id). Am getting Anonymous type data from this query.
var query = (from _vdata in Table1
join entityFind in Table2 on _vdata.id equals entityFind.id
select new { entityFind.FamilyName, entityFind.LastLogin, entityFind.GivenName,
entityFind.Email, entityFind.EmailVerified, entityFind.Uuid, _vdata.Role,
_vdata.Payers, _vdata.Accounts, _vdata.ModifiedOn }).ToList();
Am getting Anonymous data from above query. I have some more list of data in another variable i need to add those data to this list with limited columns having 4-5 columns.
How to do mapping in this situation in AutoMapper or in any other technique
thanks
Since all anonymous types derived from System.Object, I found a solution (workaround) to add mapping from object to your destination type
//Allow to map anonymous types to concrete type
cfg.CreateMap(typeof(object), typeof(ExternalCandle),
MemberList.None);
But please note that for most scenarios this is not the correct solution
For example, if you want to map ORM types - go with this way: Queryable Extensions
I guess that Jimmy bogard won't recommend this solution because of the same reason that CreateMissingTypeMaps was removed from AutoMappers's API -https://github.com/AutoMapper/AutoMapper/issues/3063
So maybe in a future version of AutoMapper this code won't work (I am using AutoMapper 10.1.1 and it worked for me)
You cannot map anonymous type. To achieve the above functionality you can create a Model like below:
public class ResultantData
{
public string FamilyName { get; set; }
public string LastLogin { get; set; }
public string GivenName { get; set; }
public string Email { get; set; }
public string EmailVerified { get; set; }
public string Uuid { get; set; }
public string Role { get; set; }
public string Payers { get; set; }
public string Accounts { get; set; }
public string ModifiedOn { get; set; }
}
Then you can write the above query as below and return the IQueryable of the result:
var query = (from _vdata in Table1
join entityFind in Table2 on _vdata.id equals entityFind.id
select new ResultantData
{
entityFind.FamilyName,
entityFind.LastLogin,
entityFind.GivenName,
entityFind.Email,
entityFind.EmailVerified,
entityFind.Uuid,
_vdata.Role,
_vdata.Payers,
_vdata.Accounts,
_vdata.ModifiedOn
});
When you want to map this result to actual model then you can use ProjectTo method of Automapper as below:
var result = query.ProjectTo<ResultantDataModel>().ToList();
I have used below class as result model:
public class ResultantDataModel
{
public string FamilyName { get; set; }
public string LastLogin { get; set; }
public string GivenName { get; set; }
public string Email { get; set; }
public string EmailVerified { get; set; }
public string Uuid { get; set; }
public string Role { get; set; }
public string Payers { get; set; }
public string Accounts { get; set; }
public string ModifiedOn { get; set; }
}
Having an issue with projection and getting child objects to load. The following is simplified code to represent the logic I'm trying to implement, not the actual code.
public class TicketItem
{
public int TicketItemId { get; set; }
public string TicketReason { get; set; }
public Station Station { get; set; }
public TicketOwner TicketOwner { get; set; }
}
public class Station
{
public int StationId { get; set; }
public string Name { get; set; }
}
public class TicketOwner
{
public int TicketOwnerId { get; set; }
public Employee Employee { get; set; }
public Organization Organization { get; set; }
}
public class Employee
{
public int EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Organization
{
public int OrganizationId { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
public class CommonReasons
{
public int CommonReasonId { get; set; }
public string Reason { get; set; }
}
public TicketItem GetById(int id)
{
var query = from i in _dataContext.TicketItems
.Include("Station")
.Include("TicketOwner.Employee")
.Include("TicketOwner.Organization")
join r in _dataContext.CommonReasons on i.TicketReason equals r.CommonReasonId.ToString() into r1
from r2 in r1.DefaultIfEmpty()
where i.TicketItemId == id
select new TicketItem {
TicketItemId = i.TicketItemId,
TicketReason = r2.Reason == null ? i.Reason : r2.Reason,
Station = i.Station,
TicketOwner = i.TicketOwner
};
return query
.AsNoTracking()
.FirstOrDefault();
}
Most the code is self-explanatory. The part that is indirectly causing the trouble would be the relationship between TicketItem.TicketReason property (a string) and the CommonReasons entity. From the user interface side, the end-user has an input field of "Reason", and they can select from "common" reasons or input an adhoc reason. They original developer chose to have the TicketReason property contain either the key ID from the CommonReasons table (if the user selected from drop-down) or the adhoc reason typed in.
So, to handle this logic in the linq query, the only way I have found is to do a left join between TicketItem.TicketReason and CommonReasons.CommonReasonId, then use projection to modify the TicketReason column returning either the common reason text or adhoc text. If there is a different way to do this that would get me around the trouble I'm having with projection/include, I'm all ears.
For the "reason" logic, this query works, returning the proper text. The trouble is that none of the "grand-child" objects are returning, i.e. TicketItem.TicketOwner.Employee, TicketItem.TicketOwner.Organization. How do I get those objects to return also?
Changing the structure of the tables would be an absolute last resort, just based on the amount of code that would have to change. There are other spots in the code that are using the above logic but don't need the child objects.
Any help would be appreciated. Hope I've explained enough.
I'm trying to correctly design the structure of an Api controller method with the following object as a return type:
var obj= new CustomObject
{
Id = a.Id,
stampleProperty= a.stampleProperty,
stampleProperty= a.stampleProperty2,
B= a.B,
};
The baseline scenario consists of two objects A and B that have a "Many to Many" relationship as described below:
public class A
{
public int AId { get; set; }
public string sampleProperty{ get; set; }
public string sampleProperty2{ get; set; }
public virtual ICollection B { get; set; }
}
public class B
{
public int BId { get; set; }
public string sampleProperty3{ get; set; }
public string sampleProperty4{ get; set; }
public int ComyId { get; set; }
public virtual ICollection A{ get; set; }
public virtual Comy Comy{ get; set; }
}
Note: I cannot change the structure of the Database. In addition, I seek the best possible way to retrieve relational B objects from A object, without B's virtual properties of A.
The code that I tried on the controller, although it uses the "LazyLoading" approach, returns embedded type A objects within each associated type B object.
var a = db.A.FirstOrDefault(a => a.stampleProperty== stampleProperty);
var obj= new CustomObject
{
Id = a.AId,
sampleProperty= a.sampleProperty,
sampleProp= a.sampleProp,
B = a.B,
};
Return:
{
"AId":
"sampleProperty":
"sampleProp":
"B":[{
"BId":
"sampleProperty3":
"sampleProperty4":
"ComyId":
"A":[ **REFERENCE LOOP** ]
"ComyId":
"Comy":{}
}]
}
Goal: B objects without Virtual properties of A.
Due to the fact that I am on the process of learning this framework I am looking for the right approach to use these tools by avoiding raw SQL queries and multiple requests.
I would suggest creating an additional custom object and mapping the fields (either manually or using a framework like AutoMapper).
Applied to your example it could look something like:
public class CustomObjectA
{
public int Id { get; set; }
public string stampleProperty { get; set; }
public string stampleProperty2 { get; set; }
public CusomObjectB[] B { get; set; }
}
public class CustomObjectB
{
public int BId { get; set; }
public string sampleProperty3{ get; set; }
public string sampleProperty4{ get; set; }
public int ComyId { get; set; }
}
And the usage would look like this:
var a = db.A.FirstOrDefault(a => a.stampleProperty== stampleProperty);
var obj= new CustomObjectA
{
Id = a.AId,
sampleProperty= a.sampleProperty,
sampleProp= a.sampleProp,
B = a.B.Select(b => new CustomObjectB
{
BId = b.BId,
sampleProperty3 = b.sampleProperty3
//etc...
}).ToArray()
};
It's not necessarily a good idea to return database entities straight from an API for these sorts of reasons (along with some others, such as you may not want third parties consuming your API to be able to see every property in the database).
A common term for this approach is the use of DTOs (Data Transfer Objects). Here's a tutorial from Microsoft where they discuss it further https://learn.microsoft.com/en-us/aspnet/web-api/overview/data/using-web-api-with-entity-framework/part-5.
EDIT: I originally worded this question very poorly, stating the problem was with JSON serialization. The problem actually happens when I'm converting from my base classes to my returned models using my custom mappings. I apologize for the confusion. :(
I'm using .NET Core 1.1.0, EF Core 1.1.0. I'm querying an interest and want to get its category from my DB. EF is querying the DB properly, no problems there. The issue is that the returned category has a collection with one interest, which has one parent category, which has a collection with one interest, etc. When I attempt to convert this from the base class to my return model, I'm getting a stack overflow because it's attempting to convert the infinite loop of objects. The only way I can get around this is to set that collection to null before I serialize the category.
Interest/category is an example, but this is happening with ALL of the entities I query. Some of them get very messy with the loops to set the relevant properties to null, such as posts/comments.
What is the best way to address this? Right now I'm using custom mappings that I wrote to convert between base classes and the returned models, but I'm open to using any other tools that may be helpful. (I know my custom mappings are the reason for the stack overflow, but surely there must be a more graceful way of handling this than setting everything to null before projecting from base class to model.)
Classes:
public class InterestCategory
{
public long Id { get; set; }
public string Name { get; set; }
public ICollection<Interest> Interests { get; set; }
}
public class Interest
{
public long Id { get; set; }
public string Name { get; set; }
public long InterestCategoryId { get; set; }
public InterestCategory InterestCategory { get; set; }
}
Models:
public class InterestCategoryModel
{
public long Id { get; set; }
public string Name { get; set; }
public List<InterestModel> Interests { get; set; }
}
public class InterestModel
{
public long Id { get; set; }
public string Name { get; set; }
public InterestCategoryModel InterestCategory { get; set; }
public long? InterestCategoryId { get; set; }
}
Mapping functions:
public static InterestCategoryModel ToModel(this InterestCategory category)
{
var m = new InterestCategoryModel
{
Name = category.Name,
Description = category.Description
};
if (category.Interests != null)
m.Interests = category.Interests.Select(i => i.ToModel()).ToList();
return m;
}
public static InterestModel ToModel(this Interest interest)
{
var m = new InterestModel
{
Name = interest.Name,
Description = interest.Description
};
if (interest.InterestCategory != null)
m.InterestCategory = interest.InterestCategory.ToModel();
return m;
}
This is returned by the query. (Sorry, needed to censor some things.)
This is not .NET Core related! JSON.NET is doing the serialization.
To disable it globally, just add this during configuration in Startup
services.AddMvc()
.AddJsonOptions(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
}));
edit:
Is it an option to remove the circular references form the model and have 2 distinct pair of models, depending on whether you want to show categories or interests?
public class InterestCategoryModel
{
public long Id { get; set; }
public string Name { get; set; }
public List<InterestModel> Interests { get; set; }
public class InterestModel
{
public long Id { get; set; }
public string Name { get; set; }
}
}
public class InterestModel
{
public long Id { get; set; }
public string Name { get; set; }
public InterestCategoryModel InterestCategory { get; set; }
public class InterestCategoryModel
{
public long Id { get; set; }
public string Name { get; set; }
}
}
Note that each of the models has a nested class for it's child objects, but they have their back references removed, so there would be no infinite reference during deserialization?
I have two classes:
public class SavedQuote
{
public int ID { get; set; }
public string Text { get; set; }
public string Context { get; set; }
public string URL { get; set; }
public string Comment { get; set; }
public string WhereToSearch { get; set; }
public DateTime DateOfAdding { get; set; }
public string OwnerName { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
public class NoteOnSite
{
public int ID { get; set; }
public string URL { get; set; }
public DateTime DateOfAdding { get; set; }
public string OwnerName { get; set; }
public string Comment { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
I have also two lists: one that represents some "SavedQuotes" and one that has some "NoteOnSites". I need to sort data from those Lists by DateOfAdding and display them in one table on my webiste.
The problem is: I (probably) can't save objects with two different classes in one List<> (I need to do this to sort those objects). What do you advise me to do? How would you solve this problem?
I (probably) can't save objects with two different classes in one List<>
You can, as long as object have a common base class. In C#, all objects have a common base class System.Object, which is enough to store objects of entirely different types in a single list.
A heavyweight approach would be to put a common interface on the objects that you wish to sort:
public interface IWithDate {
public DateTime DateOfAdding { get; set; }
}
public class SavedQuote : IWithDate {
...
}
public class NoteOnSite : IWithDate {
...
}
...
var mixedList = new List<IWithDate>();
However, this may introduce more structure than you wish: making the classes related to each other through a common interface is too much, if all you need is to sort objects of these classes together.
If you wish to sort the objects on a commonly named property without adding any static structure around your classes, you can make a list of dynamic objects, and use DateOfAdding directly:
var mixedList = new List<dynamic>();
mixedList.AddRange(quotes);
mixedList.AddRange(notes);
mixedList.Sort((a, b)=>a.DateOfAdding.CompareTo(b.DateOfAdding));
Try a little Linq using JOIN
List<SavedQuote> savedQuotes = new List<SavedQuote>();
List<NoteOnSite> noteOnSites = new List<NoteOnSite>();
var results = from savedQuote in savedQuotes.OrderBy(x => x.DateOfAdding)
join noteOnSite in noteOnSites.OrderBy(x => x.DateOfAdding)
on savedQuote.ID equals noteOnSite.ID
select new { saved = savedQuotes, note = noteOnSites };