I have a problem with testing my Context.
My app is running in .NET Core 2.2 and I've installed EFCore v2.2.6.
When I launch my test I get this error:
System.NotSupportedException : Unsupported expression: c => c.Prices
Non-overridable members (here: MyContext.get_Prices) may not be used in setup / verification expressions.
This is my context class:
using MyProject.Model;
using Microsoft.EntityFrameworkCore;
namespace MyProject.Persistence
{
public class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options) : base(options) {}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Price>()
.HasKey(p => new { p.CustomerAccount, p.ItemId, p.Amount });
}
public DbSet<Price> Prices { get; set; }
}
}
This is my repository:
using MyProject.Model;
using MyProject.Persistence;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MyProject.Repository
{
public class PriceRepository : IPriceRepository
{
private readonly MyContext _myContext;
public PriceRepository(MyContext myContext)
{
_myContext = myContext;
}
public async Task<List<Price>> GetPricesAsync(List<string> items, string customerAccount)
=> await _myContext.Prices.Where(price => price.CustomerAccount == customerAccount && items.Contains(price.ItemId)).ToListAsync();
}
}
My Price class:
[Table("Price")]
public class Price
{
public string CustomerAccount { get; set; }
public string ItemId { get; set; }
public double Amount { get; set; }
[NotMapped]
public int Id { get; set; }
[NotMapped]
public string ItemInternalId { get; set; }
[NotMapped]
public DateTime ModifiedDateTime { get; set; }
}
My test:
[Fact]
public async Task Test1Async()
{
IQueryable<Price> prices = new List<Price>
{
new Price
{
Amount = 39.71,
CustomerAccount = "010324",
ItemId = "10103001",
Id = 1,
ItemInternalId = "test",
ModifiedDateTime = new System.DateTime()
},
new Price
{
Amount = 57.09,
CustomerAccount = "010324",
ItemId = "10103001",
Id = 2,
ItemInternalId = "test2",
ModifiedDateTime = new System.DateTime()
}
}.AsQueryable();
var mockSet = new Mock<DbSet<Price>>();
var options = new DbContextOptionsBuilder<MyContext>()
.UseInMemoryDatabase(databaseName: "FekaConnectionString")
.Options;
mockSet.As<IQueryable<Price>>().Setup(m => m.Provider).Returns(prices.Provider);
mockSet.As<IQueryable<Price>>().Setup(m => m.Expression).Returns(prices.Expression);
mockSet.As<IQueryable<Price>>().Setup(m => m.ElementType).Returns(prices.ElementType);
mockSet.As<IQueryable<Price>>().Setup(m => m.GetEnumerator()).Returns(prices.GetEnumerator());
var mockContext = new Mock<MyContext>(options);
mockContext.Setup(c => c.Prices).Returns(mockSet.Object);
var repository = new PriceRepository(mockContext.Object);
var list = new List<string>
{
"10103001"
};
var result = await repository.GetPricesAsync(list, "010324");
Assert.Single(result);
}
Can anyone help me?
Thanks :)
No need to mock the context if using in-memory.
[Fact]
public async Task Test1Async() {
//Arrange
var prices = new List<Price> {
new Price {
Amount = 39.71,
CustomerAccount = "010324",
ItemId = "10103001",
Id = 1,
ItemInternalId = "test",
ModifiedDateTime = new System.DateTime()
},
new Price
{
Amount = 57.09,
CustomerAccount = "010324",
ItemId = "10103001",
Id = 2,
ItemInternalId = "test2",
ModifiedDateTime = new System.DateTime()
}
};
var options = new DbContextOptionsBuilder<MyContext>()
.UseInMemoryDatabase(databaseName: "FekaConnectionString")
.Options;
var context = new MyContext(options);
//populate
foreach(var price in prices) {
context.Prices.Add(price);
}
await context.SaveChangesAsync();
var repository = new PriceRepository(mockContext.Object);
var list = new List<string>
{
"10103001"
};
//Act
var result = await repository.GetPricesAsync(list, "010324");
//Assert
Assert.Single(result);
}
Related
I try Automapper and it is very nice but is it possible map two OuterDto source to OuterModel destination with same object InnerDeto like in code? How can I do that dest1.Inner and dest2.Inner after map has same instance? What I know, I think it is not possible. What do you think? Thanks for help me
public class OuterDto
{
public int Value { get; set; }
public InnerDto Inner { get; set; }
}
public class InnerDto
{
public int OtherValue { get; set; }
}
public class OuterModel
{
public int Value { get; set; }
public InnerModel Inner { get; set; }
}
public class InnerModel
{
public int OtherValue { get; set; }
}
public class test
{
public test()
{
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<InnerDto, InnerModel>().ReverseMap();
cfg.CreateMap<OuterDto, OuterModel>().ReverseMap();
});
config.AssertConfigurationIsValid();
InnerDto innerSource = new InnerDto { OtherValue = 15 };
var source1 = new OuterDto
{
Value = 1,
Inner = innerSource
};
var source2 = new OuterDto
{
Value = 2,
Inner = innerSource
};
var mapper = config.CreateMapper();
source1.Inner.OtherValue = 20;
var dest1 = mapper.Map<OuterDto, OuterModel>(source1);
var dest2 = mapper.Map<OuterDto, OuterModel>(source2);
dest1.Inner.OtherValue = 1000;
//Result:
//dest1.Inner.OtherValue = 1000
//dest2.Inner.OtherValue = 20
//Expected Result:
//dest1.Inner.OtherValue = 1000
//dest2.Inner.OtherValue = 1000
}
}
I'm not sure, but try to instanciate OuterModel before calling Map method
//...
var mapper = config.CreateMapper();
source1.Inner.OtherValue = 20;
var dest1 = new OuterModel();
mapper.Map(source1, dest1);
mapper.Map(source2, dest1);
dest1.Inner.OtherValue = 1000;
NOTE: I haven't tested my code, it's just to give food for thought
i'm using actually abp 4.4.0, hello, I am trying to recode the https://community.abp.io/articles/creating-an-event-organizer-application-with-the-blazor-ui-wbe0sf2z but for EfCore here is the code of the tables:
public class Event : FullAuditedAggregateRoot<Guid>
{
[Required]
public string Title { get; set; }
public string Description { get; set; }
public DateTime StartTime { get; set; }
public bool IsFree { get; set; }
public ICollection<EventAttendee> Attendees { get; set; }
public Event()
{
Attendees = new Collection<EventAttendee>();
}
}
public class EventAttendee : Entity<int>
{
public Event Event { get; set; }
public Guid EventId { get; set; }
public Guid AttendeeId { get; set; }
}
Here the DbContextModelCreatingExtensions:
builder.Entity<Event>(t =>
{
t.ToTable("Events");
t.ConfigureByConvention();
t.HasMany(x => x.Attendees)
.WithOne(x => x.Event)
.HasForeignKey(x => x.EventId)
.IsRequired(false);
});
builder.Entity<EventAttendee>(t =>
{
t.ToTable("Attendees");
t.ConfigureByConvention();
});
the DBContext referemcement
public DbSet<Event> Events { get; set; }
public DbSet<EventAttendee> Attendees { get; set; }
and the seed
public async Task SeedAsync(DataSeedContext context)
{
await _eventRepository.InsertAsync(new Event()
{
Title = "First Event",
Description = "This is a test",
IsFree = true,
StartTime = DateTime.Now.AddDays(2),
Attendees = new List<EventAttendee>()
{
new EventAttendee(){AttendeeId = Guid.NewGuid()},
new EventAttendee(){AttendeeId = Guid.NewGuid()},
new EventAttendee(){AttendeeId = Guid.NewGuid()}
}
});
}
And the EventAppService
public class EventAppService : ManagerAppService, IEventAppService
{
private readonly IRepository<Event, Guid> _eventRepository;
private readonly IRepository<IdentityUser> _userRepository;
public EventAppService(IRepository<Event, Guid> eventRepository, IRepository<IdentityUser> userRepository)
{
_eventRepository = eventRepository;
_userRepository = userRepository;
}
public async Task<EventDetailDto> GetAsync(Guid id)
{
var #event = await _eventRepository.GetAsync(id);
var attendeeIds = #event.Attendees.Select(a => a.AttendeeId).ToList();
var queryable = await _userRepository.GetQueryableAsync();
var query = queryable
.Where(u => attendeeIds.Contains(u.Id));
var attendees = (await AsyncExecuter.ToListAsync(query))
.ToDictionary(x => x.Id);
var result = ObjectMapper.Map<Event.Event, EventDetailDto>(#event);
foreach (var attendeeDto in result.Attendees)
{
attendeeDto.UserName = attendees[attendeeDto.UserId].UserName;
}
return result;
}
But i have a problem, when i execute the DBMigrator, the seed is created correctely but when i want to get my events, the attendees list is empty
{
"title": "First Event",
"description": "This is a test",
"isFree": true,
"startTime": "2021-09-23T07:48:34.663988",
"attendees": [],
"creationTime": "2021-09-21T07:48:35.656599",
"creatorId": null,
"id": "39ff1912-edee-0d2a-9aca-00a2ff5ed128"
}
and I don't understand why he can't get the attendees back, if I forgot something ?
Thank you in advance
For relational DB (EF Core), define DefaultWithDetailsFunc:
Configure<AbpEntityOptions>(options =>
{
options.Entity<Event>(eventOptions =>
{
eventOptions.DefaultWithDetailsFunc = query => query.Include(e => e.Attendees);
});
});
Alternatively, explicitly load the collection:
var #event = await _eventRepository.GetAsync(id);
await _eventRepository.EnsureCollectionLoadedAsync(#event, e => e.Attendees); // Add this
var attendeeIds = #event.Attendees.Select(a => a.AttendeeId).ToList();
Reference: https://docs.abp.io/en/abp/4.4/Entity-Framework-Core
OData url:
https://{{baseUrl}}/api/vehicles?$expand=OwnershipHistories
I want to return a vehicle(s) and than $expand the OwnershipHistories as a List of owner ship histories.
But I only get one (1) Ownershiphistorie.
Question: Why can I not get all the ownership histories???
I have this on my controller:
[HttpGet]
[EnableQuery(PageSize = 10, MaxExpansionDepth = 20)]
[ODataRoute("vehicles")]
I use this url: {{baseUrl}}/api/vehicles?$expand=OwnershipHistories
I use the following c# code ():
public async Task<IQueryable<VehicleEntity>> GetVehicles()
{
var vehicles = _context.Vehicles; // .Include(v => v.OwnershipHistories) => ??? Is not working...
return vehicles;
}
The result is:
{
"#odata.context": "https://localhost:5001/api/$metadata#Vehicles(OwnershipHistories())",
"value": [
{
"id": 1,
"registrationNumber": "10STNV",
"vehicleIdentificationNumber": "JF1BP9LLA6G052053",
// more fields
"**OwnershipHistories**": [ ?????? Only **one** record, not the **8** records I expect ?????)
{
"VweID": 1,
"registrationNumber": "10STNV",
// more fields
}
]
},
}
This is the query for the database:
I have two entities and they are views in the sql database:
OwnershipHistoryEntity:
[Table("EigenaarsHistorie", Schema = "Voertuig")]
public class OwnershipHistoryEntity
{
[Key]
public int VweID { get; set; } // VDNI
// more fields
public virtual VehicleEntity Vehicle { get; set; }
}
VehicleEntity:
namespace VWE.MijnVWE.Vehicle.Api.DAL.Entities
{
[Table("VoertuigInformatie", Schema = "Voertuig")]
public class VehicleEntity
{
[Key]
public int VweID { get; set; } // VDNI
public string Kenteken { get; set; }
public string VoertuigIdentificatieNummer { get; set; }
// more fields
[ForeignKey("VweID")]
public ICollection<OwnershipHistoryEntity> OwnershipHistories { get; set; } = new List<OwnershipHistoryEntity>();
}
}
EdmModel builder:
using Microsoft.AspNet.OData.Builder;
using Microsoft.OData.Edm;
using VWE.MijnVWE.Vehicle.Api.DAL.Entities;
namespace VWE.MijnVWE.Vehicle.Api.BLL.Builders
{
public static class ModelBuilder
{
private static IEdmModel _edmModel;
public static IEdmModel GetEdmModel()
{
return GetExplicitEdmModel();
}
static IEdmModel GetExplicitEdmModel()
{
if (_edmModel == null)
{
var modelBuilder = new ODataConventionModelBuilder();
var vehicles = modelBuilder.EntitySet<VehicleEntity>("Vehicles");
var ownershipHistories = modelBuilder.EntitySet<OwnershipHistoryEntity>("OwnershipHistories");
modelBuilder.Namespace = "vwe.mijnvwe.vehicle";
modelBuilder.ContainerName = "vehicleApiContainer";
vehicles.EntityType.Name = "vehicles";
vehicles.EntityType.HasKey(k => k.VweID);
vehicles.EntityType.HasMany(v => v.OwnershipHistories);
vehicles.HasManyBinding(v => v.OwnershipHistories, "OwnershipHistories");
ownershipHistories.EntityType.Name = "ownershipHistories";
ownershipHistories.EntityType.HasKey(k => k.VweID);
ownershipHistories.EntityType.HasRequired(o => o.Vehicle, (o, t) => o.VweID == t.VweID);
vehicles.EntityType.Property(p => p.VweID).Name = "id";
vehicles.EntityType.Property(p => p.Kenteken).Name = "registrationNumber";
vehicles.EntityType.Property(p => p.VoertuigIdentificatieNummer).Name = "vehicleIdentificationNumber";
// more fields
ownershipHistories.EntityType.Property(p => p.Kenteken).Name = "registrationNumber";
ownershipHistories.EntityType.Property(p => p.EventDatum).Name = "eventDate";
ownershipHistories.EntityType.Property(p => p.SoortAansprReferentieCode).Name = "liabilityReferenceCode";
ownershipHistories.EntityType.Property(p => p.RegistratieDatumAansprakelijkheid).Name = "from";
ownershipHistories.EntityType.Property(p => p.RegistratieDatumAansprakelijkheidTot).Name = "to";
ownershipHistories.EntityType.Property(p => p.DagenInBezit).Name = "numberOfDays";
ownershipHistories.EntityType.Property(p => p.DatumGewijzigd).Name = "changedDateTime";
_edmModel = modelBuilder.GetEdmModel();
}
return _edmModel;
}
}
}
Ef core modelBuilder:
public class VehicleDbContext : DbContext
{
public VehicleDbContext(DbContextOptions<VehicleDbContext> options)
: base(options)
{ }
public DbSet<VehicleEntity> Vehicles { get; set; }
public DbSet<OwnershipHistoryEntity> OwnershipHistories { get; set; }
// more fields
protected override void OnModelCreating(ModelBuilder builder)
{
// ...
base.OnModelCreating(builder);
}
}
This is the Select query:
SELECT [t].[VweID], [t].[AandrijvingOmschrijving],
//more fields.. [t0].[SoortAansprReferentieCode], [t0].[c0]
FROM (
SELECT TOP(#__TypedProperty_3) [v].[VweID], [v].[AandrijvingOmschrijving], [v].[AantalAangedrevenAssen],
// more fields
[v].[Werkingsbeginsel], [v].[Wielbasis]
FROM [Voertuig].[VoertuigInformatie] AS [v]
ORDER BY [v].[VweID]
) AS [t]
OUTER APPLY (
SELECT TOP(#__TypedProperty_1) #__TypedProperty_2 AS [c], [e].[VweID], [e].[DagenInBezit], [e].[DatumGewijzigd], [e].[EventDatum], [e].[Kenteken], [e].[RegistratieDatumAansprakelijkheid], [e].[RegistratieDatumAansprakelijkheidTot], [e].[SoortAansprReferentieCode], CAST(1 AS bit) AS [c0]
FROM [Voertuig].[EigenaarsHistorie] AS [e]
WHERE [t].[VweID] = [e].[VweID]
ORDER BY [e].[VweID]
) AS [t0]
ORDER BY [t].[VweID], [t0].[VweID]
It is quit similar to the following question:
Same question about washington school odata question
Washington school code odata example
I have the following model (comments define fields within given object)
public class ServiceModel
{
public List<ShippingRequest> ShippingRequest { get; set; }
public QuotesResult QuotesResult { get; set; }
}
public class ShippingRequest
{
public Address Address { get; private set; } // AddressId
public List<ShippingPackage> ShippingPackages { get; private set; }
}
public class ShippingPackage
{
public Package Package { get; private set; } // PackageId
public List<ShippingItem> ShippingItems { get; private set; } // IsSkipped
}
public class QuotesResult
{
public List<Quote> Quotes { get; set; } // PackageId, Cost
}
Suppose I have the following input, I need to get a list of AddressId's and corresponding Quotes that refer to that address (via PackageId). Quotes are already populated at this point.
Quote.PackageId = Package.PackageId
INPUT:
Suppose I have following input with three ShippingRequests
Address1 = {Package1, Package2, Package3}
Address2 = {Package5, Package8}
Address3 = {Package11, Package12}
To get the all the quotes for a given address I need to Join PackageId of "Package" with PackageId of Quote. That way I will know that this Quote belongs to this Address.
I've tried this but i get an error:
var addrQuotes = ServiceModel.ShippingRequest
.GroupJoin(ServiceModel.QuotesResult.Quotes, c1 => c1.ShippingPackages
.SelectMany(y => y.Package.Id), c2 => c2.PackageId, (c1, c2) =>
new {
c1.Address.Id,
Quotes = c2.Select(e =>
{
e.Price = c1.ShippingPackages.Any(
x => x.ShippingItems.All(y => y.IsSkipped))
? 0
: e.Price + ExtraCost;
e.Provider = GetName(e.Code);
return e;
})
}).OrderBy(q => q.Id);
One caviar to this is that I also need to check ShippingItems(s) that go in a Package. If ALL the ShippingItems within a ShippingPackage have boolean flag "IsSkipped" set to true, the Quote's Price should be set to 0, otherwise add Extra cost to Quote.Price.
OUTPUT:
Address1 = [Quote1, Quote20, Quote21, Quote50, ...]
Address2 = [Quote3, Quote100...]
Address3 = [Quote5, Quote33, Quote12]
Any help greatly appreciated.
I didn't do entire job but got something to compile and run without errors. This should get you pointed in right direction.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
ServiceModel serviceModel = new ServiceModel()
{
ShippingRequest = new List<ShippingRequest>(){
new ShippingRequest() {
Address = "Address 1",
ShippingPackages = new List<ShippingPackage>() {
new ShippingPackage() { Package = "Package1"},
new ShippingPackage() { Package = "Package2"},
new ShippingPackage() { Package = "Package3"}
}
},
new ShippingRequest() {
Address = "Address 2",
ShippingPackages = new List<ShippingPackage>() {
new ShippingPackage() { Package = "Package5"},
new ShippingPackage() { Package = "Package8"},
}
},
new ShippingRequest() {
Address = "Address 3",
ShippingPackages = new List<ShippingPackage>() {
new ShippingPackage() { Package = "Package11"},
new ShippingPackage() { Package = "Package12"},
}
}
},
QuotesResult = new QuotesResult()
{
Quotes = new List<Quote>() {
new Quote() { Cost = 123, Id = "Package1"},
new Quote() { Cost = 123, Id = "Package2"},
new Quote() { Cost = 123, Id = "Package3"},
new Quote() { Cost = 123, Id = "Package11"},
new Quote() { Cost = 123, Id = "Package11"}
}
}
};
var addrQuotes = (from requests in serviceModel.ShippingRequest.Select(x => x.ShippingPackages.Select(y => new { address = x.Address, package = y})).SelectMany(z => z)
join quote in serviceModel.QuotesResult.Quotes
on requests.package.Package equals quote.Id
select new { quote = quote, package = requests }).ToList();
var results = addrQuotes.GroupBy(m => m.package.address)
.Select(n => new {
quotes = n.Select(c => c).Select(c1 => new {
address = c1.package.address,
quote = c1.quote
}).ToList()
}).ToList();
}
}
public class ServiceModel
{
public List<ShippingRequest> ShippingRequest { get; set; }
public QuotesResult QuotesResult { get; set; }
}
public class ShippingRequest
{
public string Address { get; set; } // AddressId
public List<ShippingPackage> ShippingPackages { get; set; }
}
public class ShippingPackage
{
public string Package { get; set; } // PackageId
public List<string> ShippingItems { get; set; } // IsSkipped
}
public class QuotesResult
{
public List<Quote> Quotes { get; set; } // PackageId, Cost
}
public class Quote
{
public string Id { get; set; }
public decimal Cost { get; set; }
}
}
I need to pass complex or List<> class object to a web API in C#, without AJAX or jQuery. This is my code I used for the API and controller.
Web API:
[AcceptVerbs("GET", "POST")]
[HttpGet]
public void WriteBusinessObjectApiFromObj(int Pa1, string Pa2, object item)
{
// To Do In Function
}
Controller:
public void WriteBusinessObjectApi(object obj)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:8080/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var apiUrl = "api/WriteBusinessObjectApiFromObj?Pa1=1&Pa2=2";
var response = client.PostAsJsonAsync(apiUrl, obj).Result;
client.Dispose();
if (response.IsSuccessStatusCode)
{
}
}
}
I try to call the API but I can't send objects to it.
This object I try to send like:
Returned list of object from this function:
public List<Product> GetProductsObj()
{
var products = new List<Product>
{
new Product()
{
Id = 4,
Name = "product 4",
SupProducts = new List<SupProduct>
{
new SupProduct()
{
Id = 41,
Name = "Sup 41"
},
new SupProduct()
{
Id = 42,
Name = "Sup 42"
},
new SupProduct()
{
Id = 43,
Name = "Sup 43"
}
}
},
new Product()
{
Id = 5,
Name = "product 5",
SupProducts = new List<SupProduct>
{
new SupProduct()
{
Id = 51,
Name = "Sup 51"
},
new SupProduct()
{
Id = 52,
Name = "Sup 52"
},
new SupProduct()
{
Id = 53,
Name = "Sup 53"
}
}
},
new Product()
{
Id = 6,
Name = "product 6",
SupProducts = new List<SupProduct>
{
new SupProduct()
{
Id = 71,
Name = "Sup 71"
},
new SupProduct()
{
Id = 72,
Name = "Sup 72"
},
new SupProduct()
{
Id = 73,
Name = "Sup 73"
}
}
}
};
return products;
}
It's possible that the binder is getting confused and trying to incorrectly mix FromBody and FromURI so try wrapping all of the parameters in an object instead. This is a better pattern anyway because you're explicitly stating what it is that makes a "request". Note also that in the below I specify the object type rather than just saying "object". Better to be explicit, you know the Type on both sides anyway!
public class WriteBusinessObjectApiFromObjRequest
{
public int Pa1 { get; set; }
public string Pa2 { get; set; }
public List<Product> Item { get; set; } // change to your Type
}
[AcceptVerbs("GET", "POST")]
[HttpGet]
public void WriteBusinessObjectApiFromObj(WriteBusinessObjectApiFromObjRequest request)
{
// To Do In Function
}
var apiUrl = "api/WriteBusinessObjectApiFromObj";
var response = client.PostAsJsonAsync(
apiUrl,
new WriteBusinessObjectApiFromObj { Pa1 = 1, Pa2 = "2", Item = obj })
.Result;
Here is a cool overview of how to use parameter binding that doesn't go into any technical detail. Alternatively, here is a much more in depth exploration of Web API parameter binding
Model:
public class Employee
{
[Required]
public string EmployeeId { get; set; }
public string EmployeeName { get; set; }
public int EmployeeRollNum { get; set; }
public EmployeeAddress EmployeeAddr { get; set; }
public List<EmployeeAddress> AllEmployeeAddr { get; set; }
}
public class EmployeeAddress
{
//[Required]
public string EmployeeAddressId { get; set; }
public string EmployeeAddressName { get; set; }
public List<int> AllNum { get; set; }
}
In Web API Server:
[HttpGet]
public object Get([FromUri]Employee employee)
{
if (employee != null && ModelState.IsValid)
{
// Do something with the product (not shown).
return employee;
}
return employee;
}
In Web Api Client:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace WebApiConsoleApplication1
{
public static class UrlHelpers
{
/// <summary>
/// To the query string. http://ole.michelsen.dk/blog/serialize-object-into-a-query-string-with-reflection.html
/// </summary>
/// <param name="request"> The request. </param>
/// <param name="separator"> The separator. </param>
/// <returns></returns>
/// <exception cref="System.ArgumentNullException"> request </exception>
public static string ToQueryString(this object request, string innerPropertyName = null, string separator = ",")
{
if (request == null)
{
throw new ArgumentNullException("request");
}
StringBuilder propertyQuery = new StringBuilder();
// Get all primitive properties on the object
var properties = request.GetType().GetProperties()
.Where(x => x.CanRead)
.Where(x => x.GetValue(request, null) != null)
.Where(x => !x.PropertyType.IsClass || (x.PropertyType.IsClass && x.PropertyType.FullName == "System.String"))
.ToDictionary(x => x.Name, x => x.GetValue(request, null));
foreach (KeyValuePair<string, object> kvp in properties)
{
if (string.IsNullOrEmpty(innerPropertyName))
{
propertyQuery.AppendFormat("{0}={1}", Uri.EscapeDataString(kvp.Key), Uri.EscapeDataString(kvp.Value.ToString()));
}
else
{
propertyQuery.AppendFormat("{0}.{1}={2}", Uri.EscapeDataString(innerPropertyName), Uri.EscapeDataString(kvp.Key), Uri.EscapeDataString(kvp.Value.ToString()));
}
propertyQuery.AppendFormat("&");
}
var innerClass = request.GetType().GetProperties()
.Where(x => x.CanRead)
.Where(x => x.GetValue(request, null) != null && x.PropertyType.IsClass && x.PropertyType.FullName != "System.String" && !(x.GetValue(request, null) is IEnumerable))
.ToDictionary(x => x.Name, x => x.GetValue(request, null));
// Get names for all IEnumerable properties (excl. string)
var propertyCollectionNames = request.GetType().GetProperties()
.Where(x => x.CanRead)
.Where(x => x.GetValue(request, null) != null)
.ToDictionary(x => x.Name, x => x.GetValue(request, null))
.Where(x => !(x.Value is string) && x.Value is IEnumerable)
.ToDictionary(x => x.Key, x => x.Value);
// Concat all IEnumerable properties into a comma separated string
foreach (var kvp in propertyCollectionNames)
{
var valueType = kvp.Value.GetType();
var valueElemType = valueType.IsGenericType
? valueType.GetGenericArguments()[0]
: valueType.GetElementType();
if (valueElemType.IsPrimitive || valueElemType == typeof(string)) // List of primitive value type or string
{
var enumerable = kvp.Value as IEnumerable;
int count = 0;
foreach (object obj in enumerable)
{
if (string.IsNullOrEmpty(innerPropertyName))
{
propertyQuery.AppendFormat("{0}[{1}]={2}", Uri.EscapeDataString(kvp.Key), count, Uri.EscapeDataString(obj.ToString()));
}
else
{
propertyQuery.AppendFormat("{0}.{1}[{2}]={3}", Uri.EscapeDataString(innerPropertyName), Uri.EscapeDataString(kvp.Key), count, Uri.EscapeDataString(obj.ToString()));
}
count++;
propertyQuery.AppendFormat("&");
}
}
else if (valueElemType.IsClass) // list of class Objects
{
int count = 0;
foreach (var className in kvp.Value as IEnumerable)
{
string queryKey = string.Format("{0}[{1}]", kvp.Key, count);
propertyQuery.AppendFormat(ToQueryString(className, queryKey));
count++;
}
}
}
foreach (var className in innerClass)
{
propertyQuery.AppendFormat(ToQueryString(className.Value, className.Key));
}
return propertyQuery.ToString();
}
}
public class Employee
{
public string EmployeeId { get; set; }
public string EmployeeName { get; set; }
public int EmployeeRollNum { get; set; }
public EmployeeAddress EmployeeAddr { get; set; }
public List<EmployeeAddress> AllEmployeeAddr { get; set; }
}
public class EmployeeAddress
{
//[Required]
public string EmployeeAddressId { get; set; }
public string EmployeeAddressName { get; set; }
public List<int> AllNum { get; set; }
}
internal class Program
{
private static void Main()
{
RunAsync().Wait();
Console.WriteLine("Completed");
Console.ReadLine();
}
private static async Task RunAsync()
{
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(ConfigurationManager.AppSettings.Get("HostURL"));
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(ConfigurationManager.AppSettings.Get("AcceptType")));
HttpResponseMessage response = await client.GetAsync("api/Values/1");
if (response.IsSuccessStatusCode)
{
string val = await response.Content.ReadAsAsync<string>();
Console.WriteLine("{0}\t", val);
}
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(ConfigurationManager.AppSettings.Get("AcceptType")));
//client.DefaultRequestHeaders.Add()
// HTTP GET
Employee emp = new Employee();
emp.EmployeeId = "sri1";
emp.AllEmployeeAddr = new List<EmployeeAddress>();
EmployeeAddress lstemployeeAddr = new EmployeeAddress();
lstemployeeAddr.EmployeeAddressId = "Address1";
lstemployeeAddr.AllNum = new List<int>();
lstemployeeAddr.AllNum.Add(1);
lstemployeeAddr.AllNum.Add(2);
lstemployeeAddr.AllNum.Add(3);
emp.AllEmployeeAddr.Add(lstemployeeAddr);
lstemployeeAddr = new EmployeeAddress();
lstemployeeAddr.EmployeeAddressId = "Address2";
lstemployeeAddr.AllNum = new List<int>();
lstemployeeAddr.AllNum.Add(24);
lstemployeeAddr.AllNum.Add(56);
emp.AllEmployeeAddr.Add(lstemployeeAddr);
EmployeeAddress innerEmployeeAddr = new EmployeeAddress();
innerEmployeeAddr.EmployeeAddressId = "Addr3";
innerEmployeeAddr.AllNum = new List<int>();
innerEmployeeAddr.AllNum.Add(5);
innerEmployeeAddr.AllNum.Add(6);
emp.EmployeeAddr = innerEmployeeAddr;
string query = emp.ToQueryString();
response = await client.GetAsync("api/Values?" + query);
if (response.IsSuccessStatusCode)
{
Employee product = await response.Content.ReadAsAsync<Employee>();
Console.WriteLine("{0}\t{1}\t", product.EmployeeId, product.EmployeeName);
}
}
}
catch (Exception ex)
{
}
}
}
}