Query dynamic schema using a JSON object - GraphQL .NET 6 PostgreSQL and HotChocolate - postgresql

I need to develop a graphql server to query data directly from a JSON object. this JSON object is stored in a postgres database table as below.
This value field can be any JSON object. I don't have control over that. it's directly coming from a SharePoint server. I need to query this JSON object dynamically using graphql.
What I want here is to query JSON object and get only what I need instead of getting all the JSON data. for example like below
query {
allBookings {
id,
listId
widget {
text {
name
style
onMouseUp
}
}
}
}
currently, my result is this.
Technologies I am using
.NET 6
Postgresql
HotChocolate
this is my code so far.
[Table("bookings")]
public class Booking
{
[Column(name: "id")]
public int Id { get; set; }
[Column(name: "list_id")]
public Guid ListId { get; set; }
[Column(name: "value", TypeName = "jsonb")]
[GraphQLType(typeof(AnyType))]
public string Value { get; set; }
}
public class BookingType : ObjectType<Booking>
{
private readonly IDbContextFactory<DemoDBContext> _factory;
public BookingType(IDbContextFactory<DemoDBContext> factory)
{
_factory = factory;
}
[Obsolete]
protected override void Configure(IObjectTypeDescriptor<Booking> descriptor)
{
descriptor.Field(t => t.Id)
.Type<NonNullType<IntType>>();
descriptor.Field(t => t.ListId)
.Type<NonNullType<UuidType>>();
descriptor.Field(t => t.Value)
.Type<AnyType>()
.Resolver(context =>
{
var db = _factory.CreateDbContext();
var value = context.Parent<Booking>().Value;
return value;
});
}
}
public class Query
{
private readonly IDbContextFactory<DemoDBContext> _factory;
public Query(IDbContextFactory<DemoDBContext> factory)
{
_factory = factory;
}
public async Task<IEnumerable<Booking>> GetAllBookings()
{
using var context = await _factory.CreateDbContextAsync();
var bookings = await context.Bookings.ToListAsync();
return bookings;
}
public async Task<Booking> GetBooking(Guid id)
{
using var context = await _factory.CreateDbContextAsync();
var booking = await context.Bookings.Where(x => x.ListId == id).FirstOrDefaultAsync();
return booking;
}
}
I've tried different methods but I don't have a clear idea to implement this kind of behavior or even is this possible to do.
if there's a better way of doing this please suggest me. Thanks all.

GraphQL will automatically filter out fields that are either not requested or not part of a model.
If you define your types as:
type Booking {
id: ID!
listID: String
value: Widget
}
type Widget {
text: SubWidget
}
type SubWidget {
name: String
style: String
onMouseUp: String
}
query allBookings: [Booking]
In your resolver you're going to return an array of JSON objects corresponding to each Booking. If that JSON has fields that are not part of your type definitions they will not be returned. If some of the fields you ask for are missing then they will come back as undefined unless you make them non-nullable (ex: name: String!)
So you're most of the way there.

Related

How can get a list data of Google Sheets based on column names in Entity Framework

I'm modeling data search in Google Sheets using API (EF). I am currently connected to Google Sheets data. I also wrote a search based on RowId it's ok. Everything works fine. However I can't find data based on Id. Everything I have:
ItemGoogleSheet.cs
public class ItemGoogleSheet
{
public string Id { get; set; }
public string Name { get; set; }
}
ItemsGoogleSheetMapper.cs
public class ItemsGoogleSheetMapper
{
public static List<ItemGoogleSheet> MapFromRangeData(IList<IList<object>> values)
{
var items = new List<ItemGoogleSheet>();
foreach (var value in values)
{
ItemGoogleSheet item = new()
{
Id = value[0].ToString(),
Name = value[1].ToString(),
};
items.Add(item);
}
return items;
}
public static IList<IList<object>> MapToRangeData(ItemGoogleSheet item)
{
var objectList = new List<object>() { item.Id, item.Name };
var rangeData = new List<IList<object>> { objectList };
return rangeData;
}
}
ItemsGoogleSheetVATController.cs
public class ItemsGoogleSheetVATController : ControllerBase
{
const string SPREADSHEET_ID = "xxxx";
const string SHEET_NAME = "xx";
SpreadsheetsResource.ValuesResource _googleSheetValues;
public ItemsGoogleSheetVATController(GoogleSheetsHelper googleSheetsHelper)
{
_googleSheetValues = googleSheetsHelper.Service.Spreadsheets.Values;
}
[HttpGet("{rowId}")]
public IActionResult GetRowID(int rowId)
{
var range = $"{SHEET_NAME}!A{rowId}:AG{rowId}";
var request = _googleSheetValues.Get(SPREADSHEET_ID, range);
var response = request.Execute();
var values = response.Values;
return Ok(ItemsGoogleSheetMapper.MapFromRangeData(values).FirstOrDefault());
}
[HttpGet]
public IActionResult GetID(string id)
{
//How to get Data from Id
//return Ok();
}
}
My Google Sheets Data:
As in my description. I want to find Id = 0102 then it will output a list of results of: 0102, 01022101, 01022102
How can I get list of data based on Id column. Asking for any solutions from everyone. Thank you!
I have solved the problem. Thank you!

EF6: materializing data using custom type

Assuming I have an entity:
public class Event
{
...
public DateTime At { get; set; }
}
As well as a model object:
public class Something
{
...
public Date At { get; set; }
}
where Date is a custom type which can be implicitly converted from a DateTime:
public struct Date
{
public Date(DateTime value)
{
...
}
...
public static implicit operator Date(DateTime value)
{
return new Date(value);
}
}
What I'm trying to do is to fetch the data from database into the model:
var events = db.Events
.Select(x => new Something
{
...
At = x.At,
})
.ToList();
And of course this is failing with the following exception:
Unable to cast the type 'System.DateTime' to type 'SMG.Web.Date'. LINQ to Entities only supports casting EDM primitive or enumeration types.
Yes, I know that I could achieve what I need with the help of some temporary object, materialize DateTime value into memory and then convert it into my custom type. But what if I prefer not to use this strategy, and want to do this in one step.
The question is: Is there a way to teach LINQ to Entities how to treat my custom Date on that side of reality?
Your cast is the wrong way around. Rewrite it as:
public struct Date
{
public Date(DateTime value)
{
DateTimeValue = value;
}
// Or whatever your field or property is called
public DateTime DateTimeValue { get; }
public static implicit operator DateTime(Date date)
{
return date.DateTimeValue;
}
}
Then EF should let you use it in expressions. This approach will not work for code-first EF.

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

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

Dynamic way to Generate EntityTypeConfiguration : The type 'TResult' must be a non-nullable value type

I was thinking to generate EntityTypeConfiguration dynamically from run time and i don't want any EF dependency in Models[That is why i avoid Data Annotation].
So I declare a custom attribute(or can be any configuration file later on)
[AttributeUsage(AttributeTargets.Property, AllowMultiple=true )]
public class PersistableMemberAttribute : Attribute
{
public bool Iskey;
public bool IsRequired;
public bool IsIgnored;
public bool IsMany;
public string HasForeignKey;
public bool PropertyIsRequired;
public bool PropertyIsOptional;
}
And here is one of my Models is look like:
public class Blog
{
[PersistableMember(Iskey=true)]
public Guid BlogId { get; set; }
[PersistableMember(PropertyIsRequired = true)]
public string Name { get; set; }
public string Url { get; set; }
[PersistableMember(IsIgnored=true)]
public int Rating { get; set; }
[PersistableMember(IsMany =true)]
public ICollection<Post> Posts { get; set; }
}
Now I am going to write a generic EntityTypeConfiguration , which will create the configuration dynamically on run time based on the attribute values :
public class GenericEntityConfiguration<T> : EntityTypeConfiguration<T> where T : class
{
public GenericEntityConfiguration()
{
var members = typeof(T).GetProperties();
if (null != members)
{
foreach (var property in members)
{
var attrb= property.GetCustomAttributes(typeof( PersistableMemberAttribute ),false).OfType<PersistableMemberAttribute>();
if (attrb != null && attrb.Count() > 0)
{
foreach (var memberAttributute in attrb)
{
if (memberAttributute.Iskey || memberAttributute.IsIgnored)
{
var entityMethod = this.GetType().GetMethod("Setkey");
entityMethod.MakeGenericMethod(property.PropertyType)
.Invoke(this, new object[] { property, memberAttributute });
}
if (memberAttributute.IsRequired)
{
var entityMethod = this.GetType().GetMethod("SetRequired");
entityMethod.MakeGenericMethod(property.PropertyType)
.Invoke(this, new object[] { property, memberAttributute });
}
if (memberAttributute.PropertyIsRequired || memberAttributute.PropertyIsOptional)
{
var entityMethod = this.GetType().GetMethod("SetPropertyConfiguration");
entityMethod.MakeGenericMethod(property.PropertyType)
.Invoke(this, new object[] { property, memberAttributute });
}
}
}
}
}
}
public void SetPropertyConfiguration<TResult>(PropertyInfo propertyInfo, PersistableMemberAttribute attribute)
{
var functorParam = Expression.Parameter(typeof(T));
var lambda = Expression.Lambda(
Expression.Property(functorParam, propertyInfo)
, functorParam);
if (attribute.PropertyIsRequired)
{
this.Property<TResult>((Expression<Func<T, TResult>>)lambda).IsRequired();
}
if (attribute.PropertyIsOptional)
{
this.Property<TResult>((Expression<Func<T, TResult>>)lambda).IsOptional();
}
}
public void Setkey<TResult>(PropertyInfo propertyInfo, PersistableMemberAttribute attribute)
{
var functorParam = Expression.Parameter(typeof(T));
var lambda = Expression.Lambda(
Expression.Property(functorParam, propertyInfo)
, functorParam);
if (attribute.Iskey)
{
this.HasKey<TResult>((Expression<Func<T,TResult>>)lambda);
}
if (attribute.IsIgnored)
{
this.Ignore<TResult>((Expression<Func<T, TResult>>)lambda);
}
}
public void SetRequired<TResult>(PropertyInfo propertyInfo, PersistableMemberAttribute attribute) where TResult : class
{
var functorParam = Expression.Parameter(typeof(T));
var lambda = Expression.Lambda(
Expression.Property(functorParam, propertyInfo)
, functorParam);
if (attribute.IsRequired)
{
this.HasRequired<TResult>((Expression<Func<T, TResult>>)lambda);
}
}
}
But i got the compilation error of
Error 1 The type 'TResult' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Data.Entity.ModelConfiguration.Configuration.StructuralTypeConfiguration.Property(System.Linq.Expressions.Expression>)' D:\R&D\UpdateStorePOC\UpdateStorePOC\Data\GenericEntityConfiguration.cs 63 17 UpdateStorePOC
which for these two statements:
this.Property<TResult>((Expression<Func<T, TResult>>)lambda).IsRequired();
this.Property<TResult>((Expression<Func<T, TResult>>)lambda).IsOptional();
that means that I need to put a constraint on my method to restrict it to a value type. In C#, this is done with the ‘struct’ keyword.
public void SetPropertyConfiguration<TResult>(PropertyInfo propertyInfo, PersistableMemberAttribute attribute) Where TResult : struct
But Its not the solution since my property type can be a class e.g string or int, bool double, etc . So it is not at all clear that I can send them into this method. Please help me to solve this issue whether there is any other way to do it.
I don't want any EF dependency in models.
With fluent mapping you're almost there and you won't come any closer. Your attributes, even though intended to be moved to a configuration file, don't make your model any more free of any EF footprint.1 Worse, they only add a second mapping layer (if you like) between your model and EF's mapping. I only see drawbacks:
You still have to maintain meta data for your model, probably not any less than regular fluent mapping and (probably) in awkward manually edited XML without compile-time checking.
You will keep expanding your code to cover cases that EF's mapping covers but yours doesn't yet.2 So it's a waste of energy: in the end you'll basically have rewritten EF's mapping methods.
You'll have to keep your fingers crossed when you want to upgrade EF.
With bugs/problems you're on your own: hard to get support from the community.
So my answer to your question help me to solve this issue would be: use fluent mapping out of the box. Keep it simple.
1 For example, you would still have to use the virtual modifier to enable proxies for lazy loading.
2 Like support for inheritance, unmapped foreign keys, max length, db data type, ... this could go on for a while.

How to correctly model loosely-typed properties in RavenDB

I am new to RavenDB and looking for guidance on the correct way to store loosely-typed data. I have a type with a list of key/value pairs. The type of the value property isn't known at design time.
public class DescriptiveValue
{
public string Key { get; set; }
public object Value { get; set; }
}
When I query a DescriptiveValue that was saved with a DateTime or Guid Value, the deserialized data type is string. Numeric values appear to retain their data types.
Is there an elegant solution to retain the data type or should I simply store all values as strings? If I go the string route, will this limit me when I later want to sort and filter this data (likely via indexes?)
I hoping this is a common problem that is easily solved and I'm just thinking about the problem incorrectly. Your help is much appreciated!
UPDATE:
The output of this unit test is: Assert.AreEqual failed. Expected:<2/2/2012 10:00:01 AM (System.DateTime)>. Actual:<2012-02-02T10:00:01.9047999 (System.String)>.
[TestMethod]
public void Store_WithDateTime_IsPersistedCorrectly()
{
AssertValueIsPersisted<DateTime>(DateTime.Now);
}
private void AssertValueIsPersisted<T>(T value)
{
ObjectValuedAttribute expected = new ObjectValuedAttribute() { Value = value };
using (var session = this.NewSession())
{
session.Store(expected);
session.SaveChanges();
}
TestDataFactory.ResetRavenDbConnection();
using (var session = this.NewSession())
{
ObjectValuedAttribute actual = session.Query<ObjectValuedAttribute>().Single();
Assert.AreEqual(expected.Value, actual.Value);
}
}
I would expect actual to be a DateTime value.
Absolutely - that's one of the strength of schema-less document databases. See here: http://ravendb.net/docs/client-api/advanced/dynamic-fields
The problem is that RavenDB server has no notion of the type of Value. When sending your object to the server, Value is persisted as a string, and when you later query that document, the deserializer does not know about the original type, so Value is deserialized as a string.
You can solve this by adding the original type information to ObjectValuedAttribute:
public class ObjectValuedAttribute {
private object _value;
public string Key { get; set; }
public object Value {
get {
// convert the value back to the original type
if (ValueType != null && _value.GetType() != ValueType) {
_value = TypeDescriptor
.GetConverter(ValueType).ConvertFrom(_value);
}
return _value;
}
set {
_value = value;
ValueType = value.GetType();
}
}
public Type ValueType { get; private set; }
}
In the setter of Value we also store the type of it. Later, when getting back the value, we convert it back to its original type.
Following test passes:
public class CodeChef : LocalClientTest {
public class ObjectValuedAttribute {
private object _value;
public string Key { get; set; }
public object Value {
get {
// convert value back to the original type
if (ValueType != null && _value.GetType() != ValueType) {
_value = TypeDescriptor
.GetConverter(ValueType).ConvertFrom(_value);
}
return _value;
}
set {
_value = value;
ValueType = value.GetType();
}
}
public Type ValueType { get; private set; }
}
[Fact]
public void Store_WithDateTime_IsPersistedCorrectly() {
AssertValueIsPersisted(DateTime.Now);
}
private void AssertValueIsPersisted<T>(T value) {
using (var store = NewDocumentStore()) {
var expected = new ObjectValuedAttribute { Value = value };
using (var session = store.OpenSession()) {
session.Store(expected);
session.SaveChanges();
}
using (var session = store.OpenSession()) {
var actual = session
.Query<ObjectValuedAttribute>()
.Customize(x => x.WaitForNonStaleResults())
.Single();
Assert.Equal(expected.Value, actual.Value);
}
}
}
}