nPoco - Get single object with nested data - c#

I have two Dto's:
[TableName("Address")]
public class AddressDto
{
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string Street { get; set; }
public string Building { get; set; }
public string Appartment { get; set; }
public string ZipCode { get; set; }
public string Floor { get; set; }
public DateTime CreatedDate { get; set; }
}
[TableName("DistributionPoint")]
public class DistributionPointDto
{
[Column("Id")]
public int Id { get; set; }
public string Name { get; set; }
[Reference(ReferenceType.Foreign, ColumnName = "AddressId", ReferenceMemberName = "Id")]
public AddressDto Address { get; set; }
}
How can I get DistributionPointDto with nested AddressDto using nPoco?
I have a generic repository for CRUD with method:
public T FindById<T>(int id)
{
return _db.SingleById<T>(id);
}
But, when I'm trying to get DistributionPointDto, AddressDto is null

Curious if you got your code to work, but I have found that you need to use Query instead of SingleById to include referenced properties, and then you can use an Include statement/clause, however I don't think generics work with this, you have to explicitly mention the referenced property name, which is AddressDTO for you.
_db.Query<DistributionPointDTO>()
.Include(x => x.AddressDTO)
.Where(x => x.Id == id).First();

Related

How unflattening commands to complex types

I'm not yet dependent to either Mapster or AutoMapper. For now I'm using handwritten mappings because I couldn't find a mapper who could do this with smaller code.
The problem is how do we map flatten structures to complex objects? I think a lot of people could benefit from a good mapping example for such a complex object. I've got even a mapping condition based on CopyOfficeAddressAsInvoiceAddress whether or not the office address needs to be copied as invoice address. I've looked all over the place but couldn't get it to work.
Maybe I should also use a different naming to make it more clear for the mapping algorithm?!
The biggest question could such a map being resolved by a mapper or is this to complex? Al the demo's I've seen were using dto and model objects that are quite similar to each other. I didn't get the point of mapping an object to another object that 99% similar to each other.
I have a Command (I'm using Mediatr) that looks like as follows:
public class Command : IRequest<IActionResult>
{
public string AccountName { get; set; }
public string ContactFirstName { get; set; }
public string ContactLastName { get; set; }
public string ContactEMail { get; set; }
public string ContactPhoneNumber { get; set; }
public string BankAccount { get; set; }
public string Bank { get; set; }
public string OfficeName { get; set; }
public string OfficeAddressStreet { get; set; }
public int OfficeAddressStreetNumber { get; set; }
public string? OfficeAddressStreetNumberAddition { get; set; }
public string OfficeAddressPostalcode { get; set; }
public string OfficeAddressCity { get; set; }
public string OfficeAddressCountry { get; set; }
public string? OfficeInvoiceAddressStreet { get; set; } = null;
public int? OfficeInvoiceAddressStreetNumber { get; set; } = null;
public string? OfficeInvoiceAddressStreetNumberAddition { get; set; } = null;
public string? OfficeInvoiceAddressPostalcode { get; set; } = null;
public string? OfficeInvoiceAddressCity { get; set; } = null;
public string? OfficeInvoiceAddressCountry { get; set; } = null;
//[Ignore]
public bool? CopyOfficeAddressAsInvoiceAddress { get; set; } = false;
public string? AssociationIdentifier { get; set; } = null;
}
And I want it to be mapped to the following models:
public class Account
{
public int Id { get; set; }
public string AccountName { get; set; }
public IList<Contact> Users { get; set; }
public IList<Office> Offices { get; set; }
public string Bank { get; set; }
public string BankAccount { get; set; }
public string? AssociationIdentifier { get; set; }
}
public class Office
{
public int Id { get; set; }
public string Name { get; set; }
public Address ContactAddress { get; set; }
public Address InvoiceAddress { get; set; }
public bool HeadQuarter { get; set; }
}
public class Address
{
public int Id { get; set; }
public string Street { get; set; }
public string Postalcode { get; set; }
public int StreetNumber { get; set; }
public string StreetNumberAddition { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
public class Contact
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EMail { get; set; }
public string PhoneNumber { get; set; }
}
First of all, my experience is mainly using Automapper, and it is definitely possible to map complex types like this.
But your command does not need to be completely flat. There is nothing inherently wrong with DTOs being similar to your domain models. Using Automapper this is fairly easy as properties with the same name are mapped 1:1.
It could be that you are submitting a form with all the properties flattened in one object. In that case you could define either a seperate map for this object and each domain object.
CreateMap<AccountDto, Account>(); // mapping logic omitted
CreateMap<AccountDto, Office>();
...
Or you could map the one object to a range of objects using Tuples.
CreateMap<AccountDto, (Account, Office, ...)>(); // mapping logic omitted
But if you define seperate DTOs and make mapping profiles for them, it will probably ease your whole mapping experience. For copying the address, you can simply do something like this, in that case.
if (copyAddress)
{
office.InvoiceAddress = _mapper.Map<Address>(addressDto);
}

Linq query to get value from nested object

I need to pull a specific value from a nested object without using a foreach loop. I think the right approach here is a linq query, but I'm unable to grab the value I need. Considering the class structure:
public class Order
{
public int OrderID { get; set; }
public List<OrderItems> { get; set; }
}
public class OrderItems
{
public int OrderItemID { get; set; }
public string ItemName { get; set; }
public int Quantity { get; set; }
public List<OrderItemShipping> OrderItemShippings { get; set; }
}
public class OrderItemShipping
{
public int OrderItemShippingID { get; set; }
public Address ShipAddress { get; set; }
public class Address
{
public int AddressID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address1 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
}
I want to be able to do something like:
var shipToAddress = Order.OrderItems.OrderItemShipping.FirstOrDefault(x => x.Address.Address1);
But my syntax must not be correct, because I'm unable to grab the value I need?
If you need to access items of (nested) collections SelectMany is your friend:
var shipToAddress = Order.OrderItems
.SelectMany(oi => oi.OrderItemShipping.Select(ois => ois.ShipAddress.Address1)))
.FirstOrDefault();
Your syntax was wrong because the overload of FirstOrDefault expects a predicate(so a function that returns a bool) but you were passing: FirstOrDefault(x => x.Address.Address1).
If you need to filter it somehow("specific value from a nested object") you need to explain your requirement more precisely.

Property of one type map to another type of instance

I am using automapper for mapping view models and entity models with each other, all was good, but now i have a little different scenario where AutoMapper is not able to map my types.
My View Model:
public class CriminalSearchViewModel
{
public CriminalSearchParamsViewModel SearchParameters { get; set; }
public SelectList GenderSelectList { get; set; }
public SelectList NationalitySelectList { get; set; }
public SelectList CrimeSelectList { get; set; }
public SelectList CriminalStatusSelectList { get; set; }
}
second view model:
public class CriminalSearchParamsViewModel
{
[Required]
public string FirstName { get; set; }
public string LastName { get; set; }
public int? GenderID { get; set; }
public int? StatusID { get; set; }
public string CNIC { get; set; }
public int? AgeFrom { get; set; }
public int? AgeTo { get; set; }
public double? Height { get; set; }
public int Weight { get; set; }
public int? NationalityID { get; set; }
}
and my Business Model:
public class CriminalSearch
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int? GenderID { get; set; }
public int? StatusID { get; set; }
public string CNIC { get; set; }
public int? AgeFrom { get; set; }
public int? AgeTo { get; set; }
public double? Height { get; set; }
public int Weight { get; set; }
public int? NationalityID { get; set; }
}
I have defined mapping like:
Mapper.CreateMap<CriminalSearch, CriminalSearchParamsViewModel>();
also tried this as well:
Mapper.CreateMap<CriminalSearchParamsViewModel,CriminalSearchViewModel>()
.ForMember(dest => dest.SearchParameters, opt =>
opt.MapFrom(src => Mapper.Map<CriminalSearchParamsViewModel, CriminalSearch>(src)));
and in controller i am trying like:
public ActionResult Search(CriminalSearchViewModel searchVM)
{
if (ModelState.IsValid)
{
var searchParams = searchVM.SearchParameters;
var criminalSearch = AutoMapper.Mapper.Map<CriminalSearch>(searchParams);
_criminalService.SearchCriminals(criminalSearch);
}
return View();
}
But it always throws exception:
Missing type map configuration or unsupported mapping.
Mapping types:
CriminalSearchParamsViewModel -> CriminalSearch
NationalCriminals.UI.ViewModels.CriminalSearchParamsViewModel -> NationalCriminals.Core.Models.CriminalSearch
Destination path:
CriminalSearch
Source value:
NationalCriminals.UI.ViewModels.CriminalSearchParamsViewModel
Anybody can pint me what is going wrong?
You just need to change the order of the generic args in the method CreateMap:
Mapper.CreateMap<CriminalSearchParamsViewModel,CriminalSearch>()
Thats because the first generic arg is the Source type and the second is the Destination, it is not two way, you must to declare the both if you want to map from a type to another and viceversa like this:
Mapper.CreateMap<CriminalSearchParamsViewModel,CriminalSearch>()
Mapper.CreateMap<CriminalSearch,CriminalSearchParamsViewModel>()
The method CreateMap is described like this:
AutoMapper.Mapper.CreateMap<SourceClass, DestinationClass>();
Suggest: Using AutoMapper: Creating Mappings

Getting the most recent linked entity in a different entity

Sorry for the confusing title, if anyone knows a better summary please edit.
I have a Premise and a Person. A person can own a premise, which is then called a Property.
This is what my entities look like:
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
public string FirstName { get; set; }
public virtual List<Property> Properties { get; set; }
}
public class Premise
{
public int PremiseId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
public class Property
{
public virtual Premise Premise { get; set; }
public virtual Person Owner { get; set; }
public DateTime FromDate { get; set; }
public DateTime ToDate { get; set; }
}
So now I can easily get the properties a person has / had.
But can I get the CurrentOwner from a Premise as well?
The current owner would be the Person of the most recent Property.
I'd think it'd go something like this:
public class Premise
{
public int PremiseId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public virtual Person CurrentOwner
{
get
{
return Properties.Where(p => p.FromDate => today && p.ToDate <= today).FirstOrDefault();
}
}
}
But that won't work because I don't have a Properties list near.

Entity Framework MVC 4 Class Issue

I am trying to build a code first database with the Entity Framework with ASP.Net MVC 4.
I'm new to MVC & Entity Framework and I'm struggling with how to design my Class Objects.
I want a Members Class Like the one that follows, that has a data property of the AddressInformation Class :-
public class Member
{
public virtual int MemberID { get; set; }
public virtual string Forename { get; set; }
public virtual string Surname { get; set; }
public virtual int age { get; set; }
public virtual AddressInformation Address { get; set; }
public virtual string EmailAddress { get; set; }
public virtual string HomePhoneNumber { get; set; }
public virtual string MobileNumber { get; set; }
}
public class AddressInformation
{
public virtual int MemberID { get; set; }
public virtual string HouseNoName { get; set; }
public virtual string StreetName { get; set; }
public virtual string Town { get; set; }
public virtual string County { get; set; }
public virtual string PostCode { get; set; }
public virtual string Country { get; set; }
}
I also have another class that inherits from DbContext :-
public class CentralDataStore :DbContext
{
public DbSet<Member> Members { get; set; }
public DbSet<AddressInformation> AddressInfo { get; set; }
}
When I add the controller I am not getting the abilty to enter AddressInformation, only members info has populated through to my View's.
Anyone suggest the best method to attack this with? As I say, I'm new to MVC.
You do not need to make all of your properties virtual, only the ones used for navigation. And you need to setup up the relationship between Member and AddressInformation using the Fluency API. Also your primary key needs to be named Id or use an attribute or Fluency API to specify it is a primary key. You are also missing the id for mapping the Member to the AddressInformation. Here is what your class definition should look like.
public class Member
{
public int ID { get; set; }
public string Forename { get; set; }
public string Surname { get; set; }
public int age { get; set; }
public virtual int AddressId { get; set; }
public virtual AddressInformation Address { get; set; }
public string EmailAddress { get; set; }
public string HomePhoneNumber { get; set; }
public string MobileNumber { get; set; }
}
public class AddressInformation
{
public int ID { get; set; }
public string HouseNoName { get; set; }
public string StreetName { get; set; }
public string Town { get; set; }
public string County { get; set; }
public string PostCode { get; set; }
public string Country { get; set; }
}
Note I added the property AddressId to provide the mapping to the AddressInformation object/table. Configure the relationships in the Fluency API like this.
public class MemberConfig : EntityTypeConfiguration<Member>
{
internal MemberConfig()
{
this.HasKey(m => m.ID);
this.HasRequired(m => m.Address)
.WithRequiredDependent(a => a.ID)
.HasForeignKey(m => m.AddressId);
}
}
By setting up the foreign key relationship EF will automatically load the AddresssInformation into the Member object.
As far as I know the standard templates for generating the views does not implement the input fields for nested objects. But there is an option to expand the standard templates of MVC applications like in this link. There you can add the generation of input fields for nested classes if you are a fimilar to T4 templates.
You must be careful using this template, you can EASILY get a stackoverflow
Especially when using Entity Framework, when you have two entities with navigation properties that point to each other
The default Object template prevents recursion to a specific depth to prevent an infinite loop. I didn't like this so I wrote my own:
/Views/Shared/object.cshtml
#model object
#using System.Text;
#using System.Data;
#{
ViewDataDictionary viewData = Html.ViewContext.ViewData;
TemplateInfo templateInfo = viewData.TemplateInfo;
ModelMetadata modelMetadata = viewData.ModelMetadata;
System.Text.StringBuilder builder = new StringBuilder();
string result;
// DDB #224751
if (templateInfo.TemplateDepth > 2)
{
result = modelMetadata.Model == null ? modelMetadata.NullDisplayText
: modelMetadata.SimpleDisplayText;
}
foreach (ModelMetadata propertyMetadata in modelMetadata.Properties
.Where(pm => pm.ShowForEdit
&& pm.ModelType != typeof(System.Data.EntityState)
&& !templateInfo.Visited(pm)))
{
builder.Append(Html.Editor(propertyMetadata.PropertyName).ToHtmlString());
}
result = builder.ToString();
}
#Html.Raw(result)

Categories