Map two models with a matching property to a new model - c#

I'll try to explain this the best that I can so it makes sense.
I have two Models - BuyerProfile and Producerprofile
BuyerProfile
public class BuyerProfile : IAuditTrack
{
[KeyProperty(Identity = true)]
public int Id { get; set; }
[Required]
public string UserId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public int BuyerTypeId { get; set; }
[Required]
public string Address { get; set; }
[Required]
public string City { get; set; }
[Required]
public string State { get; set; }
public string Zipcode { get; set; }
public string Description { get; set; }
[NonStored]
public string BuyerTypeDisplay { get; set; }
}
ProducerProfile
public class ProducerProfile : IAuditTrack
{
[KeyProperty(Identity = true)]
public int Id { get; set; }
[Required]
public string UserId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Address { get; set; }
[Required]
public string City { get; set; }
[Required]
public string State { get; set; }
public string Zipcode { get; set; }
public string Description { get; set; }
}
I have a simple method on my controller that retrieves all of the profiles in the database and concatenates them together.
[HttpGet]
public JsonResult GetAllProfiles()
{
var buyerProfiles = _profileService.GetAllBuyerProfiles();
var producerProfiles = _profileService.GetAllProducerProfiles();
var profiles = buyerProfiles.Concat(producerProfiles);
return Json(profiles, JsonRequestBehavior.AllowGet);
}
Now what I would like to do is be able to find every BuyerProfile and ProducerProfile that share the same UserId and merge them together into a new model that would look like this:
public class BuyerProducerprofile
{
public string UserId { get; set; }
public string BuyerName { get; set; }
public string ProducerName { get; set; }
}
The current system that I'm building only allows users to complete 1 BuyerProfile and 1 ProducerProfile.
So for example, in the result set I might have a BuyerProfile that contains the following information:
Id -> 1543
UserId -> abc123
Name -> Bob's Buying Company
and a ProducerProfile that contains the following information:
Id -> 1678
UserId -> abc123
Name -> Bob's Produce Company
I would like to be able to combine the two into my new model so that it looks something like this:
UserId -> abc123
BuyerName -> Bob's Buying Company
ProducerName -> Bob's Produce Company
I'm not sure if this is at all possible without using some kind of Nuget package but it would be awesome if I didn't have to use one that I don't already have.
I also am currently using AutoMapper to do some of my mapping but I couldn't find any documentation that shows being able to use it to do this.

what you want to do is called a join. you can do it like this
var buyerProfiles = _profileService.GetAllBuyerProfiles();
var producerProfiles = _profileService.GetAllProducerProfiles();
var combinedProfiles =
from bp in buyerProfiles
join pp in producerProfiles on bp.UserId equals pp.UserId
select new BuyerProducerprofile()
{
UserId = pp.UserId,
BuyerName = bp.Name,
ProducerName = pp.Name
}
note: if the same user can have more than one of a type of profile, this will return a result for every combination of buyer profile and producer profile that can be made for that user.
other note: this is what is called an "inner join", and it will only give you results for users that have both profiles. You can do other kinds of joins too, but syntax for those joins doesn't feel very natural, and I don't have them committed to memory. I'm sure a google search can find the syntax for you.

Related

How to Map anonymous object in Auto Mapper

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; }
}

Problems with servicestack and typelinks

I have problems with gerenating types, it returns error
500 - InvalidDataException
I can't understand whats wrong as my project builds fine and API works.
None of the types works except the metadata
https://testapi.bokamera.se/types/
Please help as i'm stuck
Regards Kristian
Here you can see my call using Postman
and here you can see my DTO causing the problem
namespace BokaMera.API.ServiceModel.Dtos
{
[Route("/customfields",
Verbs = "GET",
Summary = "Find custom fields",
Notes =
"Find custom fields defined for the bookings that the current admin user is authorized to view."
)]
[ApiResponse(HttpStatusCode.Unauthorized, "You were unauthorized to call this service")]
[ApiResponse(HttpStatusCode.Forbidden, "You have too low privilegies to call this service")]
public class CustomFieldQuery :
QueryBase<CustomFieldConfig, CustomFieldQueryResponse>
{
[ApiMember(
Description =
"One or multiple id's of custom fields to find",
ParameterType = "query",
IsRequired = false)]
public int[] Ids { get; set; }
[ApiMember(
Description =
"Company id to find custom fields for",
ParameterType = "query",
IsRequired = false)]
public Guid? CompanyId { get; set; }
[ApiMember(
Description =
"Table to which the custom field belongs to",
ParameterType = "query",
IsRequired = false)]
public string Table { get; set; }
[ApiMember(
Description =
"Active or removed fields, empty parameter includes both",
ParameterType = "query",
IsRequired = false)]
public bool? Active { get; set; }
}
public class CustomFieldQueryResponse
{
[ApiMember(Description = "Custom field id")]
public int Id { get; set; }
[ApiMember(Description = "Reference to company that owns the custom field configuration")]
public Guid CompanyId { get; set; }
[ApiMember(Description = "Group id")]
public int? GroupId { get; set; }
[ApiMember(Description = "Config id")]
public int ConfigId { get; set; }
[ApiMember(Description = "Configuration name. Example: \"Number of persons\".")]
public string ConfigName { get; set; }
[ApiMember(Description = "Field width. Example: 20")]
public int Width { get; set; }
[ApiMember(Description = "Column in database where to store the information. Example: \"TextField1\"")]
public string Column { get; set; }
[ApiMember(Description = "Custom field description. Example: \"For how many persons is this booking?\"")]
public string Description { get; set; }
[ApiMember(Description = "Data field of custom field. Valid values are: TextBox, ... Example: \"TextBox\"")]
public string DataType { get; set; }
[ApiMember(Description = "Default value of the field. Example: \"3\"")]
public string DefaultValue { get; set; }
[ApiMember(Description = "Determines if the field is required to have a value or not")]
public bool Required { get; set; }
[ApiMember(Description = "Error message shown to the user if the field data is required but not entered")]
public string MandatoryErrorMessage { get; set; }
[ApiMember(Description = "Max lenght of the field")]
public int MaxLength { get; set; }
[ApiMember(Description = "")]
public bool MultipleLineText { get; set; }
[ApiMember(Description = "Regular expression used for validation of the field")]
public string RegEx { get; set; }
[ApiMember(Description = "Error message shown if the regular expression validation failed")]
public string RegExErrorMessage { get; set; }
[ApiMember(Description = "If the custom field is active or have been removed")]
public bool Active { get; set; }
[ApiMember(Description = "Table to which the field belongs")]
public string Table { get; set; }
[ApiMember(Description = "")]
public List<CustomFieldLookupResponse> Values { get; set; }
}
[Alias("V_FreeFieldFieldLookUp")]
public class CustomFieldLookupResponse
{
[PrimaryKey]
[Alias("FieldLookupId")]
public int? Id { get; set; }
[Alias("FreeFieldId")]
public int CustomFieldId { get; set; }
[Alias("FieldlookupActive")]
public bool? Active { get; set; }
[Alias("FieldLookupGroupId")]
public int GroupId { get; set; }
[Alias("FieldlookupSortOrder")]
public int? SortOrder { get; set; }
[Alias("FieldlookupValue")]
public string Value { get; set; }
}
//Used when sending in values on a DTO request object to the services to save on the tables.
public class CustomFieldDataValue
{
public int Id { get; set; }
public string Column { get; set; }
public string Value { get; set; }
}
//Used to list all values on a DTO response object
public class CustomFieldData
{
public int Id { get; set; }
public string Column { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Value { get; set; }
}
//Used when post or put values for different DTOS, see example Resource Dtos
public class AddCustomField
{
public int Id { get; set; }
public string Value { get; set; }
}
}
And my service
namespace BokaMera.API.ServiceInterface.Services
{
public class CustomFieldService : AppServiceBase
{
public IAutoQueryDb AutoQuery { get; set; }
[Authenticate]
[RequiredRole(Role.ApplicationAdmin)]
public object Get(CustomFieldQuery request)
{
// Get the autoquery that we will append to
var q = AutoQuery.CreateQuery(request, Request.GetRequestParams());
// The user can only see his/her own time exceptions
q = q.Where(te => te.CompanyId == UserSession.CompanyId);
// Execute counts and return the query response
var response = AutoQuery.Execute(request, q);
return response;
}
}
}
If you were on v4.0.58, it had an issue with Add ServiceStack Reference which can be resolved by upgrading to any release after that.
The issue with your DTOs was because they contained illegal " character in the Metadata attributes, i.e:
[ApiMember(Description = "Configuration name. Example: \"Number of persons\".")]
public string ConfigName { get; set; }
We validate metadata attributes to prevent an attacker from potentially injecting illegal data that they could use to generate malicious in Service consumers.
So to prevent the error you can remove double quotes (") from strings in your metadata attributes, replacing them with single quotes (') is fine, otherwise you can disable token verification in your AppHost.Configure() with:
NativeTypesFeature.DisableTokenVerification = true;
Incidentally Exception details from Code Generation will be more visible in the next release from this commit, so it will be clearer to tell what the error was.
Deprecated Warnings
Please take note of the Deprecation messages (also covered in Breaking changes in each Release Notes). QueryBase has been renamed to QueryDb, e.g:
public class CustomFieldQuery :
QueryDb<CustomFieldConfig, CustomFieldQueryResponse>
Also it's better for your custom AutoQuery implementations to pass the entire IRequest instead of just the parameters, e.g:
var q = AutoQuery.CreateQuery(request, base.Request);
This has the same behavior except it also allows this AutoQuery Service to participate in Multitenancy requests.

ASP.Net MVC - Passing data between Views

I'm currently trying to create an XML based website which has access to a feed URL. The XML feed is queried by adding url parameters to the current URL, and so I am using a form with GET method to add parameters to the URL.
I currently have a property search form which will search for properties within the feed by adding xml parameters to the url like so:
/Sales/?minprice=300000&maxprice=500000
This works perfectly and the correct results are shown to the user. However, if I was to use a filter form which filtered these properties by highest price for example, the feed parameters would be removed when the filter form is submitted. The new URL after the filter would be for example:
/Sales/?priceSort=descending
As you can see, the minprice and maxprice fields have been removed completely, leaving me with unwanted properties.
Currently, to combat this I am using sessions to store the URLs for each page and then combining them to make 1 url. I understand that using sessions within MVC based applications isn't exactly recommended.
So, I'm really just wondering if there is a better way to store the url's rather than using sessions?
Any help would be much appreciated.
Thanks in advance.
SOME CODE SNIPPETS OF THE SITE:
Model and ViewModel
public class ResultsViewModel
{
public PropertyResult[] Property { get; set; }
}
public class PropertyResult
{
public int Count { get; set; }
public int Pid { get; set; }
public int RentalPeriod { get; set; }
public string Price { get; set; }
public string Address { get; set; }
public string NameNumber { get; set; }
public string SA1 { get; set; }
public string SA2 { get; set; }
public string Town { get; set; }
public string City { get; set; }
public string County { get; set; }
public string Postcode { get; set; }
public string LocationCode { get; set; }
public string PriceText { get; set; }
public string Primary1 { get; set; }
public string Secondary1 { get; set; }
public string Secondary2 { get; set; }
public string Description { get; set; }
public string Period { get; set; }
public int Bedrooms { get; set; }
public int Receptions { get; set; }
public int Bathrooms { get; set; }
public int Garages { get; set; }
public int Gardens { get; set; }
public bool Featured { get; set; }
public int Views { get; set; }
}
Controller
try
{
var xml = XElement.Load(resultsFeed);
var query = (from props in xml.Descendants("property")
select new PropertyResult
{
// property id
Pid = Convert.ToInt32(props.Attribute("id").Value),
// Rooms Count
Bedrooms = Convert.ToInt32(props.Attribute("bedrooms").Value),
Receptions = Convert.ToInt32(props.Attribute("receptions").Value),
Bathrooms = Convert.ToInt32(props.Attribute("bathrooms").Value),
Gardens = Convert.ToInt32(props.Attribute("gardens").Value),
Garages = Convert.ToInt32(props.Attribute("garages").Value),
// 1 = sales prop, 4 = lettings prop
RentalPeriod = Convert.ToInt32(props.Attribute("rentalperiod").Value),
Period = props.Attribute("period").Value,
// address
Address = props.Element("useAddress").Value,
NameNumber = props.Element("num").Value,
SA1 = props.Element("sa1").Value,
SA2 = props.Element("sa2").Value,
Town = props.Element("town").Value,
City = props.Element("city").Value,
County = props.Element("county").Value,
Postcode = props.Element("postcode").Value,
// location code
LocationCode = props.Element("locationcodes").Value,
Featured = Convert.ToBoolean(props.Attribute("featured").Value),
// description
Description = props.Element("summaryDescription").Value,
// price
Price = props.Attribute("price").Value,
PriceText = props.Element("pricetext").Value,
// images
Primary1 = "http://lb.dezrez.com/Imaging/PictureResizer.ASP?Position=1&AgentId=" + eaid + "&BranchId="+ bid + "&width=1000&Category=Primary&PropertyId=",
Secondary1 = "http://www.dezrez.com/estate-agent-software/ImageResizeHandler.do?&photoID=2&AgentID=1239&BranchID=1976&Width=1000&PropertyId=",
Secondary2 = "http://www.dezrez.com/estate-agent-software/ImageResizeHandler.do?&photoID=3&AgentID=1239&BranchID=1976&Width=1000&PropertyId=",
}).ToArray();
View
I'm currently accessing each node like so:
#Model.Property[i].Gardens
In MVC you need to pass all the needed parameters (or rely in some store as Session, Cache, Db).
So, when sorting, you are just sending the column and order... in this case, you need to post also the filter values.
The correct way to do this, is having a ViewModel with all the filters and sorting parameters... and when you return from filtering or sorting, you can render the current filters.
So, besides filling the filter inputs with the current filters, you should craft the links to take into account all the parameters. For instance: when ordering, you pass also current filters... or if you change the filters you should maintain sortorder passing it on post.
Some code:
Your ViewModel:
public class SalesFilter{
public int? MinPrice {get; set;}
public int? MaxPrice {get; set;}
public int? IdTypeOfSale {get; set;}
...
...
...
public IEnumerable<Sale> FilteredValues {get; set;}
//SelectLists if you need that your filters being DropDownLists
public SelectList TypesOfSales {get; set;}
}
Your Controller:
public ActionResult Sales(){
var model = new SalesFilter();
model.FilteredValues = db.YourSales.Where(/*your init conditions*/);
//set other initial values in your ViewModel
return View(model);
}
[HttpPost]
public ActionResult Sales(SalesFilter filters){
model.FilteredValues = db.YourSales.Where(/*use the conditions of your filter */);
model.TypesOfSales = new SelectList(db.TypesOfSales, "idType", "Name", filter.IdTypeOfSale);
return View(model);
}
Consider using a Domain Model (all your business data, etc.) and separate View Models and extension methods that will transform your domain model to a specific view model and vice versa. Decoupling your business model from your view model using the transform indirection gives you the opportunity to use simple, easy to use view models that fit your view.

How to define default values with Entity Framework C#?

Models in question:
public class EmployeeType
{
public int employeeTypeId { get; set; }
public string typeName { get; set; }
public virtual List<Employee> Employees { get; set; }
}
public class Employee
{
public int employeeId { get; set; }
public string userName { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
public string password { get; set; }
public int employeeTypeId { get; set; }
public virtual EmployeeType EmployeeTypes { get; set; }
public virtual List<PhoneNumber> PhoneNumbers { get; set; }
}
At the moment i am adding different values through:
db.EmployeeType.Add(new EmployeeType
{
typeName = "Administrator"
});
db.EmployeeType.Add(new EmployeeType
{
typeName = "Seller"
});
db.EmployeeType.Add(new EmployeeType
{
typeName = "Accountant"
});
But in a case when i have to check if the user is an administrator or etc. i have to check through linq query and find out if the id is equal to the id in the Employee table.
How could i define default records in the EmployeeType model and not add the values through multiple .Add lines, so that i could use something like this:
if (db.Employee.FirstOrDefault(o => ...).servictypeId
== EmployeeType.Administrator)
{
}
The best way to handle this would be to convert employeetypeId into an enum in EF. You can easily achieve this by "converting" the field into an enum within the EDMX. Just right click on the property in the edmx model design screen and click "convert to enum".
Firstly though, you need to create an Enum, Called UserRole :-
enum UserRole : int
{
Administrator = 1,
Manager = 2,
Client = 3
}
Now, when you want to make new UserRole's you add them in your Enum.
When it comes to adding a new user, you simply do the following :-
new User object1 { Name = "Fred", UserRole = UserRole.Client};
dbContext.Save(object1);
EF will know to save the employeeTypeId as 3 in the database.
The advantage gets better, because now you can say :-
if(User.UserRole == UserRole.Adminstartor)
{
//Do Stuff
}

Windows Azure Mobile Service: InsertAsync

I followed the Windows Azure mobile service guide given by Microsoft over here.
I create a category class which represented the category table as follows:
public class category
{
public int Id { get; set; }
//// TODO: Add the following serialization attribute.
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
//// TODO: Add the following serialization attribute.
[JsonProperty(PropertyName = "subscribers")] //Number of Subscribers
public int Subscribers { get; set; }
[JsonProperty(PropertyName = "posts")] //Number of posts inside this category
public int Posts { get; set; }
}
I then inserted an entry into the database as give:
private IMobileServiceTable<category> categoryTable = App.MobileService.GetTable<category>();
category temp = new category() { Name = "test", Posts = 1, Subscribers = 2 };
await categoryTable.InsertAsync(temp);
All worked fine till here. Then i created a users class to represent the users table as follows:
class users
{
public int Id { get; set; } //the generated ID by the mobile service.
//// TODO: Add the following serialization attribute.
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
//// TODO: Add the following serialization attribute.
[JsonProperty(PropertyName = "nusnet")]
public string NUSNET { get; set; }
[JsonProperty(PropertyName = "email")]
public string Email { get; set; }
[JsonProperty(PropertyName = "faculty")]
public string Faculty { get; set; }
}
Now when I try to add in a user:
await userTable.InsertAsync(loggedInUser);
where logged in user is the details of the user. As given in the guide, i leave the Id filed empty and during debugging I notice that the Id is set as 0.
I get an error:
NewtonSoft.JSON.JsonSerializationException: {"Error getting value from 'Id' on 'NUSocial.users'."}
I have been trying fixing this for a while now but I have no clue on what is going wrong.
I think you'll need to apply the JSON attribute to the Id property. This is a sample of what my code, doing the same thing, looks like:
[DataContract]
public class Scores
{
[JsonProperty(PropertyName = "id")]
[DataMember]
public int Id { get; set; }
[JsonProperty(PropertyName = "UserName")]
[DataMember]
public string UserName { get; set; }
...
await scoresTable.InsertAsync(new Scores
{
UserName = _userName,
Score = (int) Score
});

Categories