WCF Data Service based on EF5 Model; how to add a custom type - entity-framework

I am trying to build a WCF Data Service with a ServiceMethod returning a custom type.
This type is used as a container to transmit multiple data collection at once. I am not able to define this type as entity or complex type.
public class BrfPackageDataContainer {
public ICollection<BrfFlight> Flights {
get;
set;
}
public ICollection<BrfFlight_Info> Flight_Infos {
get;
set;
}
public ICollection<BrfInfo> Infos {
get;
set;
}
public BrfPackageDataContainer() {
this.Flights = new List<BrfFlight>();
this.Flight_Infos = new List<BrfFlight_Info>();
this.Infos = new List<BrfInfo>();
}
}
This is my ServiceMethod declaration:
[WebGet]
[SingleResult]
public FlightInfoEntities.BrfPackageDataContainer GetBrfPackage () {
var brfPackageDataContainer = new FlightInfoEntities.BrfPackageDataContainer();
brfPackageDataContainer.Demodata();
return brfPackageDataContainer;
}
I got this running when using an empty dummy DataService as data source for the service class definition. But when I use my Entity Framework Model as data source the service refuse to start because of the missing metadata for the custom type.
My question is:
How can I use an EF Model as data source AND still use my custom type as a return value for my method.

Problem solved with a workaround:
I added 3 complex types to my modell, matching the data structure of each individual result set.
Furthermore I added a container class outside the data context which uses the complex types to hold the data in one object.
I extended the context class with a custom method to handle the stored procedure call and mapping the results to the appropriate complex types.ObjectContext.Translate helps a lot...
The WCF Data Service class is instantiated with a dummy DataContext. This enables metadata creation for my custom data container class which now can be used as return type of a custom WCF Data Service Method.
The data context is instantiated when the method is called.
Data container class` public class BrfPackageDataContainer {
public Guid TransactionId {
get;
set;
}
public List<BrfFlight> Flights {
get;
set;
}
public List<BrfFlight_Info> Flight_Infos {
get;
set;
}
public List<BrfInfo> Infos {
get;
set;
}
public BrfPackageDataContainer () {
this.Flights = new List<BrfFlight>();
this.Flight_Infos = new List<BrfFlight_Info>();
this.Infos = new List<BrfInfo>();
}
}`
context extension:
public partial class FlightInfoEntities
{
public virtual BrfPackageDataContainer GetBrfPackage(int? crewId, string operatorCode, string departure, int? flightId, DateTime? stdRangeStart,
DateTime? stdRangeEnd, string requestingApplication, string requestingComputerName,
string requestingACReg, ref Guid transactionId, int? specificInfoTypeId, byte? levelOfDetail,
bool? skipLog) {
using (DbCommand command = this.Database.Connection.CreateCommand()) {
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "[dbo].[GetBrfPackage]";
...
var dataContainer = new BrfPackageDataContainer();
try {
this.Database.Connection.Open();
using (DbDataReader reader = command.ExecuteReader()) {
dataContainer.Flights = ((IObjectContextAdapter)this).ObjectContext.Translate<BrfFlight>(reader).ToList();
reader.NextResult();
dataContainer.Flight_Infos = ((IObjectContextAdapter)this).ObjectContext.Translate<BrfFlight_Info>(reader).ToList();
reader.NextResult();
dataContainer.Infos = ((IObjectContextAdapter)this).ObjectContext.Translate<BrfInfo>(reader).ToList();
}
return dataContainer;
} catch (Exception ex) {
throw ex;
}
}
WCF Data Service Method:
[WebGet]
[SingleResult]
public BrfPackageDataContainer GetBrfPackage () {
using (var brfCtx = new FlightInfoEntities()) {
Guid transactionId = new Guid();
var brfPackageDataContainer = brfCtx.GetBrfPackage(null,"4T",null,null,null,null,"test",Environment.MachineName,null,ref transactionId,null,3,false);
return brfPackageDataContainer;
}
}

Related

Model Attribute binding in PUT Web API not wokring - ASP.NET Core 3.1

I have a PUT Rest API that I want to do binding from both body and route parameters.
Code
[HttpPut("{Id}/someStuffApi")]
public ActionResult UpdateStatus([FromBody] StatusRequest StatusRequest)
{
// code ...
}
And the model class is
public class StatusRequest
{
[FromRoute(Name = "Id")]
[Required(ErrorMessage = "'Id' attribute is required.")]
public string Id { get; set; }
[FromBody]
[Required(ErrorMessage = "'Status' attribute is required.")]
public string Status { get; set; }
}
When I made a request to this API, the Id is not mapped to the model even though I added the FromRoute attribute explicitly. Any suggestions?
The [FromBody] model binding will effectively override the [FromRoute] option in your model class. This is by design (why, I'm not sure, but an MS decision). See the "[FromBody] attribute" section of this doc: https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding. As pointed out there: "When [FromBody] is applied to a complex type parameter, any binding source attributes applied to its properties are ignored." So adding the "[FromRoute]" attribute inside your model does nothing...it's ignored. You can remove both of those attributes from your model.
So the way around this is to put the route binding in the Put action as a method parameter and then manually add it to your model in the controller before using the model.
[HttpPut("{Id}/someStuffApi")]
public ActionResult UpdateStatus(int Id, [FromBody] StatusRequest StatusRequest)
{
StatusRequest.Id = Id;
// remaining code...
}
The downside to this method is that the Required attribute cannot remain on the Id parameter. It will be null at the time of model binding and if you have .Net Core 3.1 automatic model validation active, then that will always return a 422. So if you would have to manually check that yourself before adding it to the model.
If you want even more flexibility, you can look at something like the HybridModelBinding NuGet package that allows various combinations of model binding using attributes. But this is a 3rd party dependency that you may not want. (https://github.com/billbogaiv/hybrid-model-binding/)
You can use custom model binding,here is a demo:
TestModelBinderProvider:
public class TestModelBinderProvider : IModelBinderProvider
{
private readonly IList<IInputFormatter> formatters;
private readonly IHttpRequestStreamReaderFactory readerFactory;
public TestModelBinderProvider(IList<IInputFormatter> formatters, IHttpRequestStreamReaderFactory readerFactory)
{
this.formatters = formatters;
this.readerFactory = readerFactory;
}
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType == typeof(StatusRequest))
return new StatusBinder(formatters, readerFactory);
return null;
}
}
Startup.cs:
services.AddMvc()
.AddMvcOptions(options =>
{
IHttpRequestStreamReaderFactory readerFactory = services.BuildServiceProvider().GetRequiredService<IHttpRequestStreamReaderFactory>();
options.ModelBinderProviders.Insert(0, new TestModelBinderProvider(options.InputFormatters, readerFactory));
});
StatusBinder:
public class StatusBinder: IModelBinder
{
private BodyModelBinder defaultBinder;
public StatusBinder(IList<IInputFormatter> formatters, IHttpRequestStreamReaderFactory readerFactory)
{
defaultBinder = new BodyModelBinder(formatters, readerFactory);
}
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
// callinng the default body binder
await defaultBinder.BindModelAsync(bindingContext);
if (bindingContext.Result.IsModelSet)
{
var data = bindingContext.Result.Model as StatusRequest;
if (data != null)
{
var value = bindingContext.ValueProvider.GetValue("Id").FirstValue;
data.Id = value.ToString();
bindingContext.Result = ModelBindingResult.Success(data);
}
}
}
}
StatusRequest:
public class StatusRequest
{
[Required(ErrorMessage = "'Id' attribute is required.")]
public string Id { get; set; }
[Required(ErrorMessage = "'Status' attribute is required.")]
public string Status { get; set; }
}
Action:
[HttpPut("{Id}/someStuffApi")]
public ActionResult UpdateStatus(StatusRequest StatusRequest)
{
return Ok();
}
result:

Integration Testing in .NET Core 3.1 with AutoMapper, WebApplicationFactory, Entity Framework, and DTOs

We have an API with about a dozen integration tests. All the tests passed until I added some DTOs and used AutoMapper. Now, all the tests that test methods that use AutoMapper and the DTOs are failing. I have provided all the code needed to understand one of the failing tests. Also, I read a lot about AutoMapper and the following StackOverflow posts:
Integration Testing with AutoMapper fails to initialise configuration
A kind of integration testing in ASP.NET Core, with EF and AutoMapper
Startup.cs
This is our Startup.ConfigureServices(). I have tried every code block commented out and/or marked "ATTEMPTED".
public void ConfigureServices(IServiceCollection services)
{
services
.AddDbContext<OurContext>(options =>
options.UseSqlServer(Configuration["ConnectionString"]))
.AddDbContext<OurContext>()
.AddRazorPages()
.AddMvcOptions(options => options.EnableEndpointRouting = false)
.AddNewtonsoftJson(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver());
services
.AddControllersWithViews();
//ATTEMPTED
//services
// .AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
//ATTEMPTED
//MapperConfiguration mapperConfiguration = new MapperConfiguration(mc =>
//{
// mc.AddProfile(new OurProfile());
//});
//IMapper mapper = mapperConfiguration.CreateMapper();
//services
// .AddSingleton(mapper);
//ATTEMPTED
//services
// .AddAutoMapper(typeof(Startup));
//ATTEMPTED
//var assembly = typeof(Program).GetTypeInfo().Assembly;
//services
// .AddAutoMapper(assembly);
//ATTEMPTED
var assembly = typeof(Program).GetTypeInfo().Assembly;
services.AddAutoMapper(cfg =>
{
cfg.AllowNullDestinationValues = true;
cfg.CreateMap<OurModel, OurDto>()
.IgnoreAllPropertiesWithAnInaccessibleSetter();
}, assembly);
}
Controller
This is our controller.
[Route("api/[controller]")]
[ApiController]
public class OurController : ControllerBase
{
private readonly OurContext _context;
protected readonly ILogger<OurController> Logger;
private readonly IMapper _mapper;
public OurController(OurContext context, ILogger<OurController> logger,
IMapper mapper)
{
_context = context ??
throw new ArgumentNullException(nameof(context));
Logger = logger ??
throw new ArgumentNullException(nameof(logger));
_mapper = mapper ??
throw new ArgumentNullException(nameof(mapper));
}
[HttpGet]
public async Task<ActionResult<IEnumerable<OurDto>>> GetAll()
{
IQueryable<OurModel> models = _context.OurModel;
IQueryable<OurDto> dtos =
_mapper.Map<IQueryable<OurDto>>(models);
return await dtos.ToListAsync();
}
}
Profile, Model, and DTO
Profile
public class OurProfile : Profile
{
public OurProfile()
{
CreateMap<OurModel, OurDto>();
}
}
Model
public partial class OurModel
{
public string Number { get; set; }
public string Name1 { get; set; }
public string Name2 { get; set; }
public string Status { get; set; }
public DateTime? Date { get; set; }
public string Description { get; set; }
public string Comment { get; set; }
public string District { get; set; }
}
DTO
public class OurDto
{
public string Number { get; set; }
public string Name1 { get; set; }
public string Name2 { get; set; }
public string Status { get; set; }
public DateTime? Date { get; set; }
public string Description { get; set; }
public string Comment { get; set; }
public string District { get; set; }
}
Test Fixture
This is our test fixture.
public abstract class ApiClientFixture : IClassFixture<WebApplicationFactory<Startup>>
{
private readonly WebApplicationFactory<Startup> _factory;
protected abstract string RelativeUrl { get; }
protected ApiClientFixture(WebApplicationFactory<Startup> factory)
{
_factory = factory;
}
protected HttpClient CreateClient()
{
HttpClient client;
var builder = new UriBuilder();
client = _factory.CreateClient();
builder.Host = client.BaseAddress.Host;
builder.Path = $"{RelativeUrl}";
client.BaseAddress = builder.Uri;
return client;
}
}
Test
This is our test class. The single test in this test class fails.
public class Tests : ApiClientFixture
{
public Tests(WebApplicationFactory<Startup> factory) : base(factory)
{
}
protected override string RelativeUrl => "api/OurController/";
[Fact]
public async void GetAllReturnsSomething()
{
var response = await CreateClient().GetAsync("");
Assert.True(response.IsSuccessStatusCode);
}
}
When I debug the test I see that a 500 status code is returned from the URL provided to the in-memory API.
Does anybody have some suggestions? More than half of our tests currently fail, and I suspect that AutoMapper is not configured properly for integration testing.
Creating a map for IQueryable<T> is not really a good solution. In your answer you are losing proper flow of asynchronous database querying. I wrote about IQueryable<T> in a comment because you were looking for a 500 error cause. Making it work it's a one thing, making it a good solution it's another thing, however.
I'd strongly suggest to use AutoMapper ProjectTo() extension which you can use directly on a IQueryable<T> sequence. It let's you combine mapping and querying in one go. More or less it does a Select() based on your mappings, so it not only gives you proper model right away with the query result, but it also reduces the amount of columns obtained from database, which can make the query run faster. But, there are of course limitations to it, e.g. you can't use custom type converters or conditional mapping. You can read more about Project() in the documentation.
Usage:
public async Task<ActionResult<List<OurDto>>> GetAll()
{
return await _context
.OurModel
.ProjectTo<OutDto>(_mapper.ConfigurationProvider)
.ToListAsync();
}
Thanks to #Prolog for his comment. I realized that I need to map each element of the IQueryable individually, so I rewrote my Controller method.
Also, side note: IList.AsQueryable().ToListAsync() does not work, so I wrote:
IQueryable<OurDto> dtosQueryable = dtos.AsQueryable();
return await Task.FromResult(dtosQueryable.ToList());
Old Controller Method
[HttpGet]
public async Task<ActionResult<IEnumerable<OurDto>>> GetAll()
{
IQueryable<OurModel> models = _context.OurModel;
IQueryable<OurDto> dtos =
_mapper.Map<IQueryable<OurDto>>(models);
return await dtos.ToListAsync();
}
New Controller Method
public async Task<ActionResult<IEnumerable<OurDto>>> GetAll()
{
IQueryable<OurModel> models = _context.OurModel;
IList<OurDto> dtos = new List<OurDto>();
foreach (OurModel model in models)
{
OurDto dto = _mapper.Map<OurDto>(model);
dtos.Add(dto);
}
IQueryable<OurDto> dtosQueryable = dtos.AsQueryable();
return await Task.FromResult(dtosQueryable.ToList());
}

Save CodeFirst's property to Base64 string in EFCore but read back X509Certificate2 from EFCore?

The ThirdParty model contain a property Certificate. It is a datatype of X509Certificate2 for source code use, but EFCore doesn't support it, meaning I have to convert it to Base64 string for EFCore database storage. Then convert it back to X509Certificate2 when reading from EFCore database.
How do we instruct EFCore to do this automatically with whatever property mapper?
public class ThirdParty
{
public Guid ThirdPartyId { get; set; }
public X509Certificate2 Certificate { get; set; }
public string RawData { get; set; }
public DateTime CreatedDate { get; set; }
}
var certificate1 = new X509Certificate();
var thirdParty = new ThirdParty() { Certificate = certificate }
var certificate2 = thirdParty.Certificate;
One approach would be to add an additional not mapped property that will return the X509Certificate2 object that will be created out of the Base64 string that was read from database.
Therefore the CertificateString property is used only to save and read from data context and the Certificate property is for usage in your other logic.
public class ThirdParty
{
private X509Certificate2 _certificate;
public Guid ThirdPartyId { get; set; }
// to write in data context only - only set it in production code
public string CertificateString
{
get
{
// return the Base64 string - is just pseudo code
return _certificate?.ToString();
}
set
{
_certificate = new X509Certificate2(value);
}
}
// not mapped for data context - to read from data context only - only read fro
public X509Certificate2 Certificate
{
get
{
if (string.IsNullOrWhiteSpace(CertificateString))
{
return default(X509Certificate2);
}
return new X509Certificate2(CertificateString);
}
private set
{
// will be set by CertificateString
}
}
public string RawData { get; set; }
public DateTime CreatedDate { get; set; }
}
To not map a property you could use [NotMapped] as Data Annotation or .Ignore(p => p.PropertyName) in Fluent API.
I would do something like this, possibly look at Why does Json.NET fail to serialize a X509Certificate2? if you're having issues with serializing the cert.
public class Configuration : IEntityTypeConfiguration<ThirdParty>
{
public void Configure(EntityTypeBuilder<ThirdParty> builder)
{
// This Converter will perform the conversion to and from Json to the desired type
builder.Property(e => e.Certificate).HasConversion(
v => JsonConvert.SerializeObject(v, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }),
v => JsonConvert.DeserializeObject<X509Certificate2>(v, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }));
}
}

Using sets of Entity Framework entities at runtime

I have an EF6 setup against a sql server db with about 60 tables in it.
I have entities for each table. What i'm trying to do is run the same method against a set of these entities that will be known at runtime.
The method is a qa/qc routine that does some data check on particular fields that are assured to be in each table.
I guess what i want to do is make the entity a parameter to the method so i can call it consecutive times.
I would also want to make a set of entities to pass as the parameter.
something like this:
List<string> entList = new List<string>(){"Table1","Table2","Table3"};
foreach (entName in entList)
{
//create an entity with the string name
//call myQAQCMethod with the entity
}
MyQAQCMethod (entity SomeEntity)
{
//run against this entity
doQAQC(SomeEntity);
}
Can this be done? Is it a job for reflection?
EDIT
using (var context = new Context())
{
var results = context.EntityAs.Where(a => a.Prop1 == e.Prop1)
.Where(a => a.Prop2 == e.Prop2)
.Select(a => new
{
APropertyICareAbout = a.Prop1,
AnotherPropertyICareAbout = a.Prop2
}).ToArray();
}
is precisely want i want to do. The thing is I want to avoid typing this loop 60 times. I think i'm looking for a way to "feed" a set of entities to this single method.
Also, thank you very much for helping me. I'm learning a lot.
You need to abstract an interface (entity framework won't even notice):
interface IQaQcable
{
int CommonInt { get; set; }
string CommonString { get; set; }
}
public class EntityA : IQaQcable
{
public int Id { get; set; }
public int CommonInt { get; set; }
public string CommonString { get; set; }
// other properties and relations
}
public class EntityB : IQaQcable
{
public int Id { get; set; }
public int CommonInt { get; set; }
public string CommonString { get; set; }
// other properties and relations
}
// in some unknown utility class
void MyQaQcMethod<T>(T entity) where T : IQaQcable
{
doSomethingWithIQaQcableProperties(entity.CommonInt, entity.CommonString);
}
// in some unknown test class
void Test()
{
var entities = new List<IQaQcable> { new EntityA(), new EntityB() };
foreach (var e in entities)
MyQaQcMethod(e);
}
Now, you could extract a base class from which each derives that actually implements the CommonInt and CommonString properties for each entity needing them, but that can get kind of tricky with Table-Per-Type/Table-Per-Hierarchy, so I'd start with this, and then consider introducing either an abstract or concrete base class as an improvement.
EDIT
Maybe your looking for something simpler than I first thought, based on your last comment.
Let's give ourselves what the DbContext for this might look like:
class Context : DbContext
{
public virtual DbSet<EntityA> EntityAs { get; set; }
public virtual DbSet<EntityB> EntityBs { get; set; }
}
So, it could just be that you wish to do this:
using (var context = new Context())
{
var results = context.EntityAs.Where(a => a.Prop1 == e.Prop1)
.Where(a => a.Prop2 == e.Prop2)
.Select(a => new
{
APropertyICareAbout = a.Prop1,
AnotherPropertyICareAbout = a.Prop2
}).ToArray();
}
Keeping in mind, if there is some set of properties in common across entity classes, you could still do something like the following:
IEnumerable<T> MyQaQcMethod(IQueryable<T> entities, T referenceEntity) where T : IQaQcAble
{
return entities.Where(e => SomePredicate(e, referenceEntity));
}
void Test()
{
using (var context = new Context())
{
// EntityA implements IQaQcAble
var resultsForA = MyQaQcMethod(context.EntityAs, defaultEntity).ToArray();
// so does EntityB, so can call with either
var resultsForB = MyQaQcMethod(context.EntityBs, defaultEntity).ToArray();
}
}
Keep in mind, to avoid modifying the generated entity classes, you could implement the interface members — and the interface — in a separate source file using partial classes. E.g.
// IQaQcAble.cs
internal interface IQaQcAble
{
int CommonInt { get; set; }
string CommonString { get; set; }
}
// a class whose existing property names match the interface
public partial class EntityA : IQaQcAble
{
int IQaQcAble.CommonInt
{
get { return CommonInt; }
set { CommonInt = value; }
}
string IQaQcAble.CommonString
{
get { return CommonString; }
set { CommonString = value; }
}
}
// a class whose property names differ
public partial class EntityB : IQaQcAble
{
int IQaQcAble.CommonInt
{
get { return SomeOtherInt; }
set { SomeOtherInt = value; }
}
string IQaQcAble.CommonString
{
get { return SomeOtherInt.ToString(); }
set { SomeOtherInt = Convert.ToInt32(value); }
}
}

Use a base class as the return type in ApiController

Using the ASP.NET WebApi 4 RC, is it possible to have an ApiController's return type be a base class and actually return instances of derived classes? Trying to do this now results in an internal server error (500) when returning xml. Returning json using this method works correctly.
public class Base
{
public int ID { get; set; }
}
public class Derived : Base
{
public string Message { get; set; }
}
public class ValuesController : ApiController
{
public IEnumerable<Base> Get()
{
return new Derived[] {
new Derived(){ Message="test"},
new Derived(){ Message="another"}
};
}
}
It would seem that the XML serialization is what's throwing the error but all I can see is the generic 500 error.
Yes, you need to use the knowntype serialization hint:
[System.Runtime.Serialization.KnownType(typeof(Derived))]
public class Base
{
public int ID { get; set; }
}
You might want to do it programmatically
private static Type[] GetKnownType()
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
var knownTypes = new List<Type>();
foreach (var assembly in assemblies)
{
knownTypes.AddRange(assembly.GetTypes().Where(x => x.BaseType == typeof (BaseResponse)).ToArray());
}
return knownTypes.ToArray();
}
Do remember your child class MUST have a default constructor else you will get runtime serialization error.