How to effectively map all the properties of DTO class to the FiledType class using GraphQL.NET? - asp.net-core-3.1

I am using GraphQL.NET with asp.net core 3.1 to develop graphql based apis. I have the following code:
ContactDTO.cs
public class ContactDTO
{
public int ContactId { get; set; }
public int? ImageId { get; set; }
public string ImagePath { get; set; }
public string OfficePhone { get; set; }
public string OfficeFaxNumber { get; set; }
public string MobilePhoneNumber { get; set; }
public string Email { get; set; }
public string Website { get; set; }
public string FaceBookUrl { get; set; }
public string TwitterUrl { get; set; }
public string LinkedInUrl { get; set; }
public string GooglePlusUrl { get; set; }
public string WeChatUrl { get; set; }
public string WeboUrl { get; set; }
public string XINGUrl { get; set; }
public string VKUrl { get; set; }
public int? CountryId { get; set; }
public string CreatedDate { get; set; }
public string UpdatedDate { get; set; }
public string EmpGUID { get; set; }
public int? LanguageId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string NativeName { get; set; }
public string Title { get; set; }
public string Role { get; set; }
public string EmployeeLevel { get; set; }
public string Introduction { get; set; }
public string Organization { get; set; }
public string OrganizationUnitName { get; set; }
public int AddressId { get; set; }
public AddressDTO address { get; set; }
}
}
AddressDTO.cs
public class AddressDTO
{
public int AddressId { get; set; }
public string Street { get; set; }
public string Country { get; set; }
public string City { get; set; }
}
ContactType.cs
public class ContactType : ObjectGraphType<ContactDTO>, IGraphQLType
{
public ContactType()
{
Name = "Contact";
Field(co => co.ContactId, nullable: true);
Field(co => co.OfficePhone, nullable: true);
Field(co => co.OfficeFaxNumber, nullable: true);
Field(co => co.MobilePhoneNumber, nullable: true);
Field(co => co.Email, nullable: true);
Field(co => co.Website, nullable: true);
Field(co => co.FaceBookUrl, nullable: true);
Field(co => co.TwitterUrl, nullable: true);
Field(co => co.LinkedInUrl, nullable: true);
Field(co => co.GooglePlusUrl, nullable: true);
Field(co => co.WeChatUrl, nullable: true);
Field(co => co.WeboUrl, nullable: true);
Field(co => co.XINGUrl, nullable: true);
Field(co => co.VKUrl, nullable: true);
Field(co => co.FirstName, nullable: true);
Field(co => co.LastName, nullable: true);
Field(co => co.NativeName, nullable: true);
Field(co => co.Title, nullable: true);
Field(co => co.Role, nullable: true);
Field(co => co.EmployeeLevel, nullable: true);
Field(co => co.Introduction, nullable: true);
Field(co => co.Organization, nullable: true);
Field(co => co.OrganizationUnitName, nullable: true);
Field(co => co.ImagePath, nullable: true);
Field<AddressType>(
"address",
resolve: context =>
{
return context.Source.address;
}
);
}
}
In the above the ContactType.cs is very extensive class with many properties that are inherited from the ContactDTO.cs.
I am looking for a way to map all the properties of ContactDTO.cs to the ContactType.cs.
Can anyone help me to do this mapping in better way

You can make your ContactType inherit from AutoRegisteringObjectGraphType and then just add the extra fields in the constructor for which you need explicit resolvers(like the "address" field in your case)
https://github.com/graphql-dotnet/graphql-dotnet/blob/master/src/GraphQL/Types/Composite/AutoRegisteringObjectGraphType.cs

I assume, that is not so easy just inherit from base class property (to be precise - it's schema field, with own resolvers, not just simple property)
What I did - just find more elegant ready-to-use solution, that is based on GraphQL.Net, where I can set up fields in json file (or if you have tons of fields - just run script to generate that json file with db-schema cofiguration). In additional, quite simple way of defining related fields. just google something like NReco.GraphQL

Related

Many to Many relationship Entity Framework Core 5

I created a Blazor project and I have a many-to-many relationship between these classes:
public class ItemAttribute
{
[Key]
public int ItemAttributeId { get; set; }
public string Title { get; set; }
public ICollection<Item> Items { get; set; }
public ICollection<ItemAttributeCluster> itemAttributeClusters { get; set; }
}
and
public class ItemAttributeCluster
{
[Key]
public int ItemAttributeClusterId { get; set; }
public string Titel { get; set; }
public bool IsMultiChoice { get; set; }
public ICollection<ItemAttribute> itemAttributes { get; set; }
}
So far so good, EF generates the Join table ItemAttributeItemAttributeCluster, ok.
Then I try to add a new cluster of ItemAttributes for the first time with my controller:
// Create
[HttpPost]
public async Task<IActionResult> Post(ItemAttributeCluster itemAttributeCluster)
{
_context.ItemAttributeClusters.Add(itemAttributeCluster);
await _context.SaveChangesAsync();
return Ok(itemAttributeCluster);
}
and I get this error:
Cannot insert explicit value for identity column in table 'ItemAttributes' when IDENTITY_INSERT is set to OFF.
What am I doing wrong? Why is EF trying to write something into 'ItemAttributes'? When i´m trying to create a new Cluster on 'ItemAttributesCluster' and the Join Table?
Migration Builder:
Join Table
migrationBuilder.CreateTable(
name: "ItemAttributeItemAttributeCluster",
columns: table => new
{
itemAttributeClustersItemAttributeClusterId = table.Column<int>(type: "int", nullable: false),
itemAttributesItemAttributeId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ItemAttributeItemAttributeCluster", x => new { x.itemAttributeClustersItemAttributeClusterId, x.itemAttributesItemAttributeId });
table.ForeignKey(
name: "FK_ItemAttributeItemAttributeCluster_ItemAttributeClusters_itemAttributeClustersItemAttributeClusterId",
column: x => x.itemAttributeClustersItemAttributeClusterId,
principalTable: "ItemAttributeClusters",
principalColumn: "ItemAttributeClusterId",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ItemAttributeItemAttributeCluster_ItemAttributes_itemAttributesItemAttributeId",
column: x => x.itemAttributesItemAttributeId,
principalTable: "ItemAttributes",
principalColumn: "ItemAttributeId",
onDelete: ReferentialAction.Cascade);
});
ItemAttributes
migrationBuilder.CreateTable(
name: "ItemAttributes",
columns: table => new
{
ItemAttributeId = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ItemAttributes", x => x.ItemAttributeId);
});
ItemAttributeCluster
migrationBuilder.CreateTable(
name: "ItemAttributeClusters",
columns: table => new
{
ItemAttributeClusterId = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Titel = table.Column<string>(type: "nvarchar(max)", nullable: true),
IsMultiChoice = table.Column<bool>(type: "bit", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ItemAttributeClusters", x => x.ItemAttributeClusterId);
});
If this was an existing schema for the ItemAttribute / Cluster tables and their PK were defined as identity columns, you will need to tell EF to expect them using the [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] attribute alongside the Key designation.
When using a naming convention that EF recognizes like "ItemAttributeId" or "Id" I believe EF will default to assuming these are Identity columns, but with a name like "ItemAttributeCode" I believe it would assume a database generated option of "None" as default.
try to add some navigation properties
public ItemAttributeCluster()
{
AttributeClusters = new HashSet<AttributeCluster>();
}
[Key]
public int Id { get; set; }
public string Titel { get; set; }
public bool IsMultiChoice { get; set; }
[InverseProperty(nameof(AttributeCluster.ItemAttributeClaster))]
public virtual ICollection<AttributeCluster> AttributeClusters { get; set; }
}
public partial class ItemAttribute
{
public ItemAttribute()
{
AttributeClusters = new HashSet<AttributeCluster>();
}
[Key]
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<Item> Items { get; set; }
[InverseProperty(nameof(AttributeCluster.ItemAttribute))]
public virtual ICollection<AttributeCluster> AttributeClusters { get; set; }
}
public partial class AttributeCluster
{
[Key]
public int Id { get; set; }
public int ItemAttributeId { get; set; }
public int ItemAttributeClasterId { get; set; }
[ForeignKey(nameof(ItemAttributeId))]
[InverseProperty("AttributeClusters")]
public virtual ItemAttribute ItemAttribute { get; set; }
[ForeignKey(nameof(ItemAttributeClasterId))]
[InverseProperty(nameof(ItemAttributeCluster.AttributeClusters))]
public virtual ItemAttributeCluster ItemAttributeClaster { get; set;
}
dbcontext (no any fluent apis at all)
public virtual DbSet<AttributeCluster> AttributeClusters { get; set; }
public virtual DbSet<ItemAttribute> ItemAttributes { get; set; }
public virtual DbSet<ItemAttributeCluster> ItemAttributeClusters { get; set; }
Test
var itemAttributeClaster = new ItemAttributeCluster { Titel="titleClaster2", IsMultiChoice=false};
var itemAttribute = new ItemAttribute{Title="attrTitle" };
var attributeClaster = new AttributeCluster { ItemAttribute = itemAttribute, ItemAttributeClaster = itemAttributeClaster };
_context.AttributeClusters.Add(attributeClaster);
_context.SaveChanges();
it created 1 record in each of 3 tables
I give up on getting this to work with ef. I run several sql`s directly to achieve the same functionality and so far it works, not a satisfactory solution but it needs to be done.

Entity Framework Required With Optional

I have the following model where I am attempting to make the Notification property on a Request object be null or the id of a notification.
However, I am not quite sure of how to map this with the fluent mapping. HasOptional -> WithMany seems to be the closest I can get, but I'd like to ensure that the NotificationId column in Requests is unique. What is the best way to accomplish this with fluent mapping?
public class Request
{
public int RequestId { get; set; }
public string Description { get; set; }
public int? NotificationId { get; set; }
public virtual Notification Notification { get; set; }
}
public class Notification
{
public int NotificationId { get; set; }
public string Description { get; set; }
public DateTime CreateDate { get; set; }
}
public class RequestMap : EntityTypeConfiguration<Request>
{
public RequestMap()
{
HasKey(x => x.RequestId);
Property(x => x.Description).IsRequired().HasMaxLength(255);
HasOptional(x => x.Notification)
.WithWhat?
}
}
using HasOptional(x => x.Notification) is enough you don't need WithMany
you dont have many Request with the same Notification
public class Request
{
public int RequestID { get; set; }
public string Description { get; set; }
public int? NotificationId { get; set; }
public Notification Notification { get; set; }
}
public class Notification
{
public int NotificationId { get; set; }
public string Description { get; set; }
public DateTime CreateDate { get; set; }
}
public class RequestMap : EntityTypeConfiguration<Request>
{
public RequestMap()
{
HasKey(x => x.RequestID);
Property(x => x.Description).IsRequired().HasMaxLength(255);
HasOptional(x => x.Notification);
}
}
and the generated migration
public partial class initial : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.Notifications",
c => new
{
NotificationId = c.Int(nullable: false, identity: true),
Description = c.String(),
CreateDate = c.DateTime(nullable: false),
})
.PrimaryKey(t => t.NotificationId);
CreateTable(
"dbo.Requests",
c => new
{
RequestID = c.Int(nullable: false, identity: true),
Description = c.String(nullable: false, maxLength: 255),
NotificationId = c.Int(),
})
.PrimaryKey(t => t.RequestID)
.ForeignKey("dbo.Notifications", t => t.NotificationId)
.Index(t => t.NotificationId);
}
public override void Down()
{
DropForeignKey("dbo.Requests", "NotificationId", "dbo.Notifications");
DropIndex("dbo.Requests", new[] { "NotificationId" });
DropTable("dbo.Requests");
DropTable("dbo.Notifications");
}
}

Not sure how to setup entity for list

I have a user
public class UserEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
[Index(IsUnique=true)]
[Required, StringLength(50)]
public string Username { get; set; }
public List<UserTokenEntity> Tokens { get; set; }
}
that has a list of tokens and I'm not sure how to set it up so that the key to UserEntityId is not null when I generate the migration. This is my TokenEntity
public class UserTokenEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(512)]
[Required]
public string TokenHash { get; set; }
[StringLength(128)]
[Required]
public string Device { get; set; }
}
when I add a migration for these entities a UserEntity_Id is created for the database on the token entity but it is a nullable int and I want nullable to be false
here is the migration generated
public override void Up()
{
CreateTable(
"dbo.UserTokenEntities",
c => new
{
Id = c.Int(nullable: false, identity: true),
TokenHash = c.String(nullable: false, maxLength: 512),
Device = c.String(nullable: false, maxLength: 128),
UserEntity_Id = c.Int(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.UserEntities", t => t.UserEntity_Id)
.Index(t => t.UserEntity_Id);)
}
does anyone know how to set up the token entity to get the UserEntity_Id to be nullable: false without manually changing it?
You can explicitly define the relationship:
public class UserTokenEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(512)]
[Required]
public string TokenHash { get; set; }
[StringLength(128)]
[Required]
public string Device { get; set; }
public int UserEntityId { get; set; }
[ForeignKey("UserEntityId")]
public UserEntity UserEntity { get; set; }
}
https://msdn.microsoft.com/en-us/data/jj591583.aspx#Relationships

EF code first - add inverse propterty with optional element

I have the following classes
public class Order {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Required]
public DateTime Date { get; set; }
[Required]
[MaxLength(100)]
public string From { get; set; }
public int? TreatGuestEntryID { get; set; }
[ForeignKey("TreatGuestEntryID")]
public TreatedGuestEntry TreatGuestEntry { get; set; }
...
public class TreatedGuestEntry {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[MaxLength(200)]
public string Company { get; set; }
public string TypeOfTreat { get; set; }
This works as expected - in my Orders table it creates the foreign key.
Now I want to add an inverse property in TreatedGuestEntry for the order.
The best (at least somehow working) result I get when I add
modelBuilder.Entity<TreatedGuestEntry>()
.HasOptional(a => a.Order)
.WithOptionalDependent(a => a.TreatGuestEntry)
.Map(a=>a.MapKey("TreatGuestEntryID"));
and further rename the key of TreatedGuestEntry to TreatGuestEntryID.
But I get no relation in the database and also TreatGuestEntryID in the table Order is no longer a key (FK).
My approach in simple words:
In my Order I want an optional TreatedGuestEntry (and I need access to the foreign key) - and further in the related TreatedGuestEntry I want to access the Order.
In your case, the FK TreatGuestEntryID is not a PK, it means that it is a 1:n relationship. So, you have to put a Collection of Order on the other side:
public class Order
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Required]
public DateTime Date { get; set; }
[Required]
[MaxLength(100)]
public string From { get; set; }
public int? TreatGuestEntryID { get; set; }
public TreatedGuestEntry TreatGuestEntry { get; set; }
}
public class TreatedGuestEntry
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[MaxLength(200)]
public string Company { get; set; }
public string TypeOfTreat { get; set; }
public ICollection<Order> Orders { get; set; }
}
Mapping:
modelBuilder.Entity<Order>()
.HasOptional(i => i.TreatGuestEntry)
.WithMany(i => i.Orders)
.HasForeignKey(i => i.TreatGuestEntryID)
.WillCascadeOnDelete(false);
Generated Migration:
CreateTable(
"dbo.Orders",
c => new
{
ID = c.Int(nullable: false, identity: true),
Date = c.DateTime(nullable: false),
From = c.String(nullable: false, maxLength: 100),
TreatGuestEntryID = c.Int(),
})
.PrimaryKey(t => t.ID)
.ForeignKey("dbo.TreatedGuestEntries", t => t.TreatGuestEntryID)
.Index(t => t.TreatGuestEntryID);
CreateTable(
"dbo.TreatedGuestEntries",
c => new
{
ID = c.Int(nullable: false, identity: true),
Company = c.String(maxLength: 200),
TypeOfTreat = c.String(),
})
.PrimaryKey(t => t.ID);

EF Fluent API One-to-One relationship

I'm trying to configure an optional-required simple relationship, but EF doesn't seem to scaffold the right migration:
public class Applicant
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int ContactInfoId { get; set; }
public string PreferredCultureId { get; set; }
public virtual ApplicantContact Contact { get; set; }
public virtual Culture PreferredCulture { get; set; }
}
public ApplicantConfiguration()
{
HasKey(a => a.Id);
Property(a => a.FirstName).IsRequired().HasMaxLength(50);
Property(a => a.LastName).IsRequired().HasMaxLength(50);
HasOptional(a => a.Contact)
.WithRequired(c => c.Applicant)
.WillCascadeOnDelete();
HasOptional(a => a.PreferredCulture)
.WithMany()
.HasForeignKey(a => a.PreferredCultureId);
}
CreateTable(
"Main.Applicants",
c => new
{
Id = c.Int(nullable: false, identity: true),
FirstName = c.String(nullable: false, maxLength: 50),
LastName = c.String(nullable: false, maxLength: 50),
ContactInfoId = c.Int(nullable: false),
PreferredCultureId = c.String(maxLength: 128),
})
.PrimaryKey(t => t.Id)
.ForeignKey("General._Cultures", t => t.PreferredCultureId)
.Index(t => t.PreferredCultureId);
Why is ContactInfoId is not being generated as a foreign key and nullable, as it is the optional side of the relationship ?
In your domain class try
public class Applicant
{
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ApplicantContact Contact { get; set; }
public virtual Culture PreferredCulture { get; set; }
}
public class ContactInfo
{
// whatever contact info fields you have
}
public class Culture
{
// culture fields
}
then in your context have
public DbSet<ContactInfo> ContactInfos { get; set; }
public DbSet<Applicant> Applicants { get; set; }
public DbSet<Culture> Cultures { get; set; }
The Id fields should get automatically if they are int.