I use azure Cosmos DB with MongoDB api in my .net web api project. I created entity Log and repository
public class Log : EntityBase
{
public string LogId { get; private set; }
public string Message { get; private set; }
public LogTypes LogType { get; private set; }
public Log(string message, LogTypes logType, DateTime created)
{
LogId = Guid.NewGuid().ToString();
Message = message;
LogType = logType;
SetCreated(created);
}
}
public abstract class EntityBase
{
public DateTime Created { get; private set; }
public DateTime? LastModified { get; private set; }
public virtual void SetCreated(DateTime datetime)
{
Created = datetime;
}
public virtual void Update(DateTime datetime)
{
LastModified = datetime;
}
}
public class LogRepository : ILogRepository
{
private IMongoCollection<Log> _logs;
public LogRepository(IApplicationNoSqlDbContext mongoContext)
{
var database= mongoContext.GetDatabase();
_logs = database.GetCollection<Log>("Logs");
}
public async Task AddLog(Log log)
{
await _logs.InsertOneAsync(log);
}
}
Above code works, but I have a problem with "Created" property. In the database, the record looks like this:
{
"_id" : ObjectId("6009779afd752e89fb6e0949"),
"_t" : [
"EntityBase",
"Log"
],
"Created" : {
"$date" : 1611236756061
},
"LastModified" : null,
"LogId" : "772d740f-d7a6-4614-8370-d2dc9c871e24",
"Message" : "test2",
"LogType" : 0
}
Why is "Created" property has this format? How can I change it and store simply UTC format? I tried configure that in this way:
BsonClassMap.RegisterClassMap<EntityBase>(cm =>
{
cm.SetIsRootClass(true);
cm.AutoMap();
cm.MapProperty(x => x.Created).SetSerializer(DateTimeSerializer.UtcInstance);
});
But the format is the same.
Related
First of all I have read many similar topics but really I'm confused why I'm getting null refrence exception.
I have an Entity named SendCommentConfiguration :
public class SendCommentConfiguration
{
public int Id { get; set; }
public string Configuration { get; set; }
}
And DTO of it named SendCommentConfigurationDTO :
public class SendCommentConfigurationDTO
{
public int Id { get; set; }
public string Configuration { get; set; }
}
Configuration property is a json object that saved in database.
This is my mapper profile :
public class MapperProfiles : Profile
{
public MapperProfiles()
{
CreateMap<CommentDTO, Comment>().ReverseMap();
CreateMap<ReviewDTO, Review>().ReverseMap();
CreateMap<WebsiteDTO, Websites>().ReverseMap();
CreateMap<CommentGroupDTO, CommentGroup>().ReverseMap();
CreateMap<SendCommentConfigurationDTO, SendCommentConfiguration>().ReverseMap();
}
}
And here I'm mapping SendCommentConfiguration to SendCommentConfigurationDTO :
public async Task<IEnumerable<SendCommentConfigurationDTO>> GetAllSendRequests()
{
var result = await _reportsRepository.GetAllSendRequests();
var mappedResult = _mapper.Map<IEnumerable<SendCommentConfigurationDTO>>(result);
return mappedResult;
}
Result variable is not null and has 55 records of SendCommentConfiguration Model but when I'am mapping to DTO, MappedResult is null and I'm getting NullRefrence Exception.
i have this controller method that return an array of objects
public async Task<ActionResult<List<AllClientsDataModelDb>>> GetAll()
{
var ReturnValue = new List<AllClientsDataModelDb>();
ReturnValue = await Clda.GetClients(new { cm = 1 });
return (ReturnValue);
}
here is the code of AllClientsDataModelDb class
public class AllClientsDataModelDb
{
public long IDCLIENT { get; set; }
public string CL_CODE { get; set; }
public string CL_NOM { get; set; }
public string CL_ADRESSE { get; set; }
public string CL_CODEPOS { get; set; }
public string CL_VILLE { get; set; } = null;
public int CL_ETATCOMPTE { get; set; }
public int CL_AlerteCompta { get; set; }
}
but the result of that method (in browser) does not respect the case sensitivity of the class properties
Example :
[{"idclient":1,"cL_CODE":"1","cL_NOM":"EUROPEQUIPEMENTMysql","cL_ADRESSE":"ModifSoft","cL_CODEPOS":"44","cL_VILLE":"STDENIS","cL_ETATCOMPTE":1,"cL_AlerteCompta":0},
{"idclient":2,"cL_CODE":"2","cL_NOM":"A UTOMATISMES-SERVICESzzzz","cL_ADRESSE":null,"cL_CODEPOS":"97420","cL_VILLE":"LEPORT","cL_ETATCOMPTE":1,"cL_AlerteCompta":0},
what i'm doing wrong ?
You need to create your own Json Profile Formatter by inheriting from JsonOutputFormatter.
public class PascalCaseJsonProfileFormatter : JsonOutputFormatter
{
public PascalCaseJsonProfileFormatter() : base(new JsonSerializerSettings { ContractResolver = new DefaultContractResolver() }, ArrayPool<char>.Shared)
{
SupportedMediaTypes.Clear();
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse
("application/json;profile=\"https://en.wikipedia.org/wiki/PascalCase\""));
}
}
Then modify your Startup.cs file's ConfigureServices Method like this.
services.AddMvc()
.AddMvcOptions(options =>
{
options.OutputFormatters.Add(new PascalCaseJsonProfileFormatter());
});
Try this, it should work.
I'm using .NET Core and WebApi and I am trying to figure out what the url would look like to send an array of objects through.
For example
public class DataObject
{
public int id { get; set;}
public string name { get; set }
}
[HttpGet()]
public <ActionResult<string>> GetSomething(DataObject[] data))
{
//do something and return a string
}
what would the url look like to do this? Should I use FromQuery or FromRoute on data? On the HttpGet(), what should be in the parenthesis? "{data}" or something else?
Everything I can find so far has been on integer arrays or string arrays, but not complex arrays to a get call.
Update
Still not able to get this to work even though I'm sure the reply I have gotten should work. Here is some more code.
[Route("api/[controller]/[action]")]
[HttpGet()]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(GridResult), (int)HttpStatusCode.OK)]
public async Task<ActionResult<GridResult>> GetGridData<TFilter1, TFilter2, TItem1>
([FromQuery]string sessionID, [FromQuery] GridDetails details, [FromQuery] TFilter1[] TFilters1, [FromQuery] TFilter2[] TFilters2, [FromQuery] TItem1[] TSorts)
and finally the url that I have generated that throws a 404.
https://localhost:44366/api/grid/GetGridData/sessionID=598357390&details?NUMBER_OF_ROWS_FIRST_RETURNED=100&CURSOR_POSITION=0&RESULT_IN_SAXORDER=false&TERSERESPONSE=true&IsStaticList=true&GRID_TYPE=list&REQUEST_TYPE=LIST.DATA_ONLY.STORED&GRID_NAME=WUWP09&TFilters1[0].AliasName=PRO_CODE&TFilters1[0].Operator=%3D&TFilters1[0].SEQNUM=1&TFilters1[1].AliasName=APR_CLASS&TFilters1[1].Operator=%3D&Tsorts[1].SEQNUM=2&Tsorts[0].ALIAS_NAME=pvd_value&Tsorts[0].TYPE=ASC
Update 2
https://localhost:44366/api/grid/GetGridData?sessionID=598357390&details.NUMBER_OF_ROWS_FIRST_RETURNED=100&details.CURSOR_POSITION=0&details.RESULT_IN_SAXORDER=false&details.TERSERESPONSE=true&details.IsStaticList=true&details.GRID_TYPE=list&details.REQUEST_TYPE=LIST.DATA_ONLY.STORED&details.GRID_NAME=WUWP09&details.TAB_NAME&details.LOCALIZE_RESULT&details.USER_FUNCTION_NAME&details.TOTALRECORDS&details.RES_IsMoreRecords&details.RES_CURRENT_CURSOR_POSITION&TFilters1[0].AliasName=PRO_CODE&TFilters1[0].Operator=%3D&TFilters1[0].SEQNUM=1&TFilters1[1].AliasName=APR_CLASS&TFilters1[1].Operator=%3D&Tsorts[1].SEQNUM=2&Tsorts[0].ALIAS_NAME=pvd_value&Tsorts[0].TYPE=ASC
Update 3
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
var _accessor = services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
var config = new GridServices.Models.config();
Configuration.Bind("Connections", config);
services.AddSingleton(config);
services.AddSingleton(new Controllers.GridController(config));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
}
GridController
namespace EAMWebApi.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class GridController : ControllerBase
{
config Config { get; }
//private readonly LinkGenerator _linkGenerator;
public GridController(config config)
{
config = Config;
//_linkGenerator = linkGenerator;
}
[HttpGet()]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(GridResult), (int)HttpStatusCode.OK)]
public async Task<ActionResult<GridResult>> GetGridData<TFilter1, TFilter2, TItem1>
([FromQuery]string sessionID, [FromQuery] GridDetails details, [FromQuery] TFilter1[] TFilters1 = null, [FromQuery] TFilter2[] TFilters2 = null, [FromQuery] TItem1[] TSorts = null)
{//Do something}
}
GridDetails
namespace GridServices.Models
{
public class GridDetails
{
public string GRID_NAME { get; set; }
public string NUMBER_OF_ROWS_FIRST_RETURNED { get; set; }
public string CURSOR_POSITION { get; set; }
public string TAB_NAME { get; set; }
public string RESULT_IN_SAXORDER { get; set; }
public string TERSERESPONSE { get; set; }
public string LOCALIZE_RESULT { get; set; }
public string USER_FUNCTION_NAME { get; set; }
public string TOTALRECORDS { get; set; }
public bool RES_IsMoreRecords { get; set; }
public bool IsStaticList { get; set; }
public string GRID_TYPE { get; set; }
public string REQUEST_TYPE { get; set; }
public string RES_CURRENT_CURSOR_POSITION { get; set; }
}
}
MultiAddOnFilter
public class MultiAddOnFilter
{
public string ALIAS_NAME { get; set; }
public string OPERATOR { get; set; }
public string OPERATORSpecified { get; set; }
public string VALUE { get; set; }
public string LPAREN { get; set; }
public string RPAREN { get; set; }
public string JOINER { get; set; }
public string JOINERSpecified { get; set; }
public string SEQNUM { get; set; }
public MultiAddOnFilter(string _ALIAS_NAME, string _OPERATOR, string _VALUE)
{
ALIAS_NAME = _ALIAS_NAME;
OPERATOR = _OPERATOR;
OPERATORSpecified = "true";
VALUE = _VALUE;
}
}
Sorts
namespace GridServices.Models
{
public class Sort
{
public string ALIAS_NAME { get; set; }
public string TYPE { get; set; }
public string TYPESpecified { get; set; }
public Sort(string _ALIAS_NAME, string _TYPE)
{
ALIAS_NAME = _ALIAS_NAME;
TYPE = _TYPE;
TYPESpecified = "true";
}
}
}
what would the url look like to do this?
It should be something like the following:
GET /Somecontroller/GetSomething?data[0].id=1&data[0].name=nameA&data[1].id=2&data[1].name=nameB&data[2].id=3&data[2].name=nameC
This payload is almost the same as you post in application/x-www-form-urlencoded format, except you'll send it as a querystring.
[Edit]
If one of those items were to be empty, would I have to pass %00 to it to indicate a null value?
Let's say you have such an object array:
data = [
{
"id": 1,
"name": "nameA"
},
{
"id": 2,
"name": null
},
{
"id": 3,
"name": "nameC"
}
]
Note the data[1].name==null. you don't have to specify data[1].name :
?data[0].id=1&data[0].name=nameA&data[1].id=2&data[2].id=3&data[2].name=nameC
If the whole data[1] item is null, just adjust the index of data[2] to data[1]:
data[0].id=1&data[0].name=nameA&data[1].id=3&data[1].name=name
Or you could add an empty field for this item:
?data[0].id=1&data[0].name=nameA&data[1].id=&data[2].id=3&data[2].name=nameC
What if the whole DataObject was null? /GetSomething?data=%00 ?
you don't have to specify /GetSomething?data=%00, just send a request to /GetSomething?, and then you'll get an empty array.
[Edit2]
There're two reasons that always routes you to a 404 result:
You're registering your GridController as a singleton. MVC will register controllers automatically (as a scoped service). Just remove that line :
services.AddSingleton(new Controllers.GridController(config));
Your controller action of GetGridData<TFilter1, TFilter2, TItem1> is a generic method. It won't work by default. There's already a thread on SO talking about this. I would also suggest you use a specific GridFilter type for each method. If you find yourself repeating the same logic, you could put your generic method into a parent MySupperGridBaseController<TFilter1, TFilter2, TItem1> class, something as below:
public class MySupperGridBaseController<TFilter1, TFilter2, TItem1> : ControllerBase
{
public async Task<ActionResult<GridResult>> GetGridData
([FromQuery]string sessionID, [FromQuery] GridDetails details, [FromQuery] TFilter1[] TFilters1 = null, [FromQuery] TFilter2[] TFilters2 = null, [FromQuery] TItem1[] TSorts = null)
{
...
}
}
// now we could reuse the same logic inherited from parent
public class GridController : MySupperGridBaseController<MultiAddOnFilter, MultiAddOnFilter, Sort>
{
}
Let me preface this question with, I am VERY new to ASP.NET Core/EF Core.
My model look like this:
namespace MyProject.Models
{
public class DeviceContext : DbContext
{
public DeviceContext(DbContextOptions<DeviceContext> options) : base(options) { }
public DbSet<Device> Devices { get; set; }
public DbSet<DeviceLocation> DeviceLocations { get; set; }
}
public class Device
{
public int Id { get; set; }
public string DeviceName { get; set; }
public string ServerName { get; set; }
public string MacAddress { get; set; }
public string LastUpdate { get; set; }
public string WiredIPAddress { get; set; }
public string WirelessIPAddress { get; set; }
public DeviceLocation DeviceLocation { get; set; }
}
public class DeviceLocation
{
public int Id { get; set; }
public string Location { get; set; }
public virtual ICollection<Device> Devices { get; set; }
}
}
I would like to be able to fetch a specific Device based on DeviceName, but I would also like to fetch ALL the devices in a particular Location.
I think the following would work for the first question:
var _Devices = DeviceContext.Devices.FirstOrDefault(d => d.DeviceName == "BLA");
I am just having a hard time getting the second query to run. Ideally, the output would be rendered to JSON to be consumed by an API. I would like the output to look something like this:
{
"Locations": {
"NYC": ["ABC", "123"],
"Boston": ["DEF", "456"],
"Chicago": ["GHI", "789"]
}
}
UPDATE
If I use the following code, it give me the following error:
Code:
// Grouping by ProfileName
var devices = DeviceContext.DeviceLocations.Include(n => n.Device).ToList();
var result = new { success = true, message = "Successfully fetched Devices", data = devices };
return JsonConvert.SerializeObject(result);
Error:
Additional information: Self referencing loop detected for property 'DeviceLocation' with type 'Project.Models.DeviceLocation'. Path 'data[0].Device[0]'.
You can try as shown below.
Note : Use Eager Loading with Include.
using System.Data.Entity;
var devicesList = DeviceContext.DeviceLocations.Where(d=>d.Location = "Your-Location-Name")
.Include(p => p.Devices)
.ToList();
Update :
var devicesList = DeviceContext.DeviceLocations
.Include(p => p.Devices)
.ToList();
I am creating a REST service using Web API and am using EF Code First to retrieve and store data to back end that service. I have a bunch of classes defined and the EF creates the database no problems at all. I can retrieve the data and again have no issues. The problem I experience is when I try to write back a record. If I have created the following little mock up to reproduce the problem.
public class Company
{
[Key]
public int CompanyID { get; set; }
public string Name { get; set; }
}
public class Contact
{
[Key]
public int ContactID { get; set; }
public int CompanyID { get; set; }
public virtual Company Company { get; set; }
public string Name { get; set; }
}
public class Quote
{
[Key]
public int QuoteID { get; set; }
public int CustomerID { get; set; }
[ForeignKey("CustomerID")]
public virtual Company Customer { get; set; }
public int ContactID { get; set; }
public virtual Contact Contact { get; set; }
public string Reference { get; set; }
}
I then have three controllers in my REST service;
public class CompaniesController : ApiController
{
TestDBContext Context = new TestDBContext();
public IEnumerable<Company> GetCompanies()
{
return Context.Companies;
}
}
[Route("api/companies/{CompanyID}/contacts")]
public class CompanyContactsController : ApiController
{
TestDBContext Context = new TestDBContext();
public IEnumerable<Contact> GetCompanyContacts(int CompanyID)
{
return Context.Contacts.Where(C => C.CompanyID == CompanyID);
}
}
public class QuotesController : ApiController
{
TestDBContext Context = new TestDBContext();
public HttpResponseMessage PostQuote(Quote value)
{
try
{
if (ModelState.IsValid)
{
Context.Quotes.Add(value);
Context.SaveChanges();
// Return the object in the body.
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, value);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = value.QuoteID }));
return response;
}
}
catch
{
}
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
}
I pre-populate the database with a company and a contact and then have the following code to create a quote.
HttpClient Client = new HttpClient();
string CompanyURL = "http://localhost:56619/api/Companies";
string ContactURL = "http://localhost:56619/api/Companies/{0}/Contacts";
string QuoteURL = "http://localhost:56619/api/Quotes";
Client.GetAsync(CompanyURL).ContinueWith((T) =>
{
T.Result.EnsureSuccessStatusCode();
T.Result.Content.ReadAsAsync<IEnumerable<Company>>().ContinueWith((T2) =>
{
Company Comp = T2.Result.First();
Client.GetAsync(string.Format(ContactURL, Comp.CompanyID)).ContinueWith((T3) =>
{
T3.Result.EnsureSuccessStatusCode();
T3.Result.Content.ReadAsAsync<IEnumerable<Contact>>().ContinueWith((T4) =>
{
Contact Cont = T4.Result.First();
Quote Q = new Quote() { Customer = Comp, Contact = Cont, Reference = "Test" };
JsonMediaTypeFormatter Formatter = new JsonMediaTypeFormatter();
Formatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
Client.PostAsync<Quote>(QuoteURL, Q, Formatter).ContinueWith((T5) =>
{
T5.Result.EnsureSuccessStatusCode();
T5.Result.Content.ReadAsAsync<Quote>().ContinueWith((T6) =>
{
var Res = T6.Result;
});
});
});
});
});
});
I know the code is a little scrappy, but it is just thrown together to illustrate the problem without any extra stuff cluttering the code up.
When the EF saves the changes to the database it creates 1 quote, an extra contact and 2 extra company records. I'm puzzled as to why. I am using VS2013, EF6 and Web API 2.
If anyone has any suggestions they would be very gratefully be received.
Many thanks in advance,
Neil.