How to implement a Linq query by converting entities to DTOs? - c#

There are 3 entities, let's say they are presented in this way:
**1 - entity**
class A
int id ;
int Name;
[Foreign key]
int id_B;
List C;
**2 - entity**
class B
int id ;
int Name;
List A;
**3 - entity**
class C
int id;
int Name;
[Foreign Key]
int id_A;
created an entity DTO (Everything is the same only without foreign keys)
1
class ADTO
int id ;
int Name;
List C;
2
class BDTO
int id ;
int Name;
List A;
3
class CDTO
int id;
int Name;
Now the request looks like this:
var quer = (await _context.A
.Include(b => b.B)
.Include(c => c.C)
.Where(u => u.Id == 1).ToListAsync())
.Select(a => new ADto
{
Id = a.Id,
//How to get information about entity B here by converting to DTO
C = a.C.Select(cdto => new CDTO{ Id = cdto.Id, Name = cdto.Name}).ToList(),
});
How to get information about entity B here by converting to DTO?

If you are querying "A" as your top-level entity then I believe you're just missing a navigation property to it's associated "B". (As it contains the B_Id FK)
1 - entity
public class A
{
public int id { get; set; }
public string Name { get; set; }
[ForeignKey("B")]
public int id_B { get; set; }
public virtual B B { get; set; }
public virtual ICollection<C> Cs { get; set;} = new List<C>();
}
Then when you project your Entities to DTOs using Select:
var query = (await _context.A
.Where(a => a.Id == 1)
.Select(a => new ADto
{
Id = a.Id,
B = new BDTO { Id = a.B.Id /* ... */ },
Cs = a.Cs.Select(c => new CDTO{ Id = c.Id, Name = c.Name}).ToList(),
}).Single();
Note that when using .Select you do not need to use .Include to reference related entities, that is only used to eager load related entities where you want to return an entity graph. (Such as when reading the entities to update values from DTOs) Also, be wary of using any ToList operations prior to using a Select as this will load entities into memory before applying things like filters, and negates the optimization of queries to fill just what Select needs.
});

Normally, I would suggest you implement an interface, that is provided on the constructor of the resulting object
so:
public interface IDbOjbect{
int Id {get;set;}
string Name{get;set;}
}
and then on your DTO object
public Class DtoObject {
public DtoOjbect(IDbOjbect source)
{
//Mapping done here.
}
}
Because then you can implement the interface on any persistence layer object, and the mapping will still work.
Because then the linq query is simply:
DbOjbectList.Select(x => new DtoObject(x));
provided DtoOjbect implements the interface.
your C would look like this:
public partial class C {
public int id {get;set;}
public string Name {get;set;}
}
public partial class C : IDbOjbect {
}
and your CDTO would look like:
public Class CDTO{
public int Id {get;set;}
public string Name {get;set;}
public CDTO(IDbOjbect source)
{
Id = source.Id;
Name = source.name;
}
}
Want to be able to make a DTO from B?
Implement IDbOjbect on your B
by using
public partial class B {
public int id {get;set;}
public string Name {get;set;}
}
public partial class B : IDbOjbect {
}
and now any C or B can be made into a CDTO.
Best part is, you can make a generic method for your B and C, use the "Where" keyword after you generic definition, and then use the Interface as the type, now you can make a single method that does the same thing based on what has the interface implementation, and this will also work for A, if you implement the interface on A.
Without further modification.
So now that you are asking questions, you original question doesn't, lets expand.
Lets say you have a ResumeInfo Object that only B has available.
You then use the NullPointer pattern together with interface segregation principle.
So you create an interface on your resumeInfo class
Example:
public interface IResumeInfo
{
string PlaceOfEmployment {get;set;}
DateTime StartOfEmployment {get;set;}
DateTime? EndOfEmployment {get;set;}
}
Then on your ResumeInfo Object:
public partial class ResumeInfo
{
string PlaceOfEmployment {get;set;}
DateTime StartOfEmployment {get;set;}
DateTime? EndOfEmployment {get;set;}
}
public partial class ResumeInfo : IResumeInfo
{
}
Then lets say you want a single DTO object:
public class DTOUserAndResume
{
public int id {get;set;}
public string Name {get;set;}
string PlaceOfEmployment {get;set;}
DateTime StartOfEmployment {get;set;}
DateTime? EndOfEmployment {get;set;}
public DTOUserAndResume(IDbOjbect source, IResumeInfo resumeInfo)
{
Id = source.Id;
Name = source.name;
PlaceOfEmployment = resumeInfo.PlaceOfEmployment;
StartOfEmployment = resumeInfo.StartOfEmployment ;
EndOfEmployment = resumeInfo.EndOfEmployment ;
}
}
Now on B? I think you said you have resume data, but not on C?
you implement the IResumeInfo on both, but on B, you just get whatever data is there, but on C that has no data? NullOjbect Pattern.
Implement the interfacce, but make it return nothing.
So PlaceOfEmployment is always "" or Null.
Start data is always 1900-01-01 00:00:00 or whatever you want "nothing" to be on a not nullable object, and null on the end of employment.
So you simply claim that the data is the equavilant of a none-existing data set, because, it doesn't have a dataset to provide.
But you dont need to make a new DTO, you can just update the constructor on CDTO, it will also work fine. It might just get a bit confusing in regards to naming and stuff.
This should result in a call that looks like:
C = a.C.Select(cdto => new CDTO{cdto, cdto.ResumeInfo}).ToList();

Related

EF Core: loading related entities as only ids

I'm not sure what I want is something one should expect from EF, but I think I've seen this in other ORMs and this should be quite common for it to be solved in EF - so I'm asking if it has been.
I want to be able to eagerly load foreign key entities when querying EF, but only their ids, as the other part of objects will be loaded much later.
I have:
class A {
public int Id { get; set; }
public B B { get; set; }
}
class B {
public int Id { get; set; }
// .... more properties
}
And a web API, that should return a list of all As, with this view model:
class AViewModel {
public int Id { get; set; }
public int BId { get; set; }
}
I want to make sure I do not include B table join when querying - for performance reasons. I'm also using automapper to map from A to AViewModel.
Currently the best way I found to do this is:
var a = context.As;
var aList = a.Select(x => new { model = x, bid = x.B.Id }).ToList();
return Ok(mapper.Map<List<AViewModel>(aList));
Unfortunately this means that I have to add mapping from new { model = x, bid = x.B.Id } to AViewModel, that is really unconvenient.
I'd prefer to just be able to write:
var a = context.As;
var aList = a.ToList();
return Ok(mapper.Map<List<AViewModel>(aList));
But in this case it fails with NullReferenceException, because every item of aList has B property null.
I could write context.As.Include(x => x.B) but this will join B table, that I would like to avoid.
I think I have seen some ORMs being able to fill in B objects with empty objects, except for ids - and that is the behavior I'm looking for in EF. Can it do that?
If not maybe one can suggest a nicer way of solving a problem? maybe I can fix this somehow with lazy proxies?
Put the foreignKey property on the A class:
class A {
public int Id { get; set; }
public int BId {get; set;}
[ForeignKey("BId")] //can do this in the fluent API instead
public virtual B B { get; set; }
}
Then you can just use the class A in the mapping with no need to load the B entity

EntityFreamwork full entity and lite entity

i have table users
user table :
Id, Name , firstName , password , email , address , dateofBrith
i want to create two entity for user table one lite and other full
[Table("user")]
public class LiteUser
{
public int ID {get;set;}
public string Name {get;set;}
public int firstName{get;set;}
}
second entity
public class fullUser : LiteUser
{
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
but not I get error about no column discriminator
is possible to do somthing like my entity are same but one have more filed then the other entity
thank you in advance for help
Unfortunately, no. You can only define one entity to one table. Instead, you'd have to do a manual .Select off of the full entity to return a custom "Lite" entry because EF needs to know all the columns that tie to a specific table from the start.
Edit: The only way around this would be to create a view and map to that instead.
You can do something like this
[Table("user")]
public class LiteUser
{
public string Name {get;set;}
public int firstName{get;set;}
}
public class fullUser : LiteUser
{
public int ID {get;set;}
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
Use primary key public int ID {get;set;} value in the derived class
As Daniel points out, a table can be associated to a single entity definition, outside of Table Per Hierarchy inheritance, which isn't what you are looking for.
This was an old trick I used with NHibernate which isn't supported in EF.
With EF you can utilize Linq and ViewModels to avoid the need of Lite vs. Full models.
Given:
//Entity
public class User
{
public int ID {get;set;}
public string Name {get;set;}
public int firstName{get;set;}
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
// View Models...
public class LiteUserViewModel
{
public int ID {get;set;}
public string Name {get;set;}
public int firstName{get;set;}
}
public class FullUserViewModel : LiteUserViewModel
{
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
Querying..
//Give me a list of lite data..
var viewModels = context.Users
.Where(x => x.DateOfBirth < startDate)
.Select(x => new LiteUserViewModel
{
UserId = x.UserId,
Name = x.Name,
FirstName = x.FirstName
}).ToList();
// Give me a full user.
var viewModel = context.Users
.Where(x => x.UserId = userId)
.Select(x => new FullUserViewModel
{
UserId = x.UserId,
// ... etc ...
}).SingleOrDefault();
You can leverage libraries like AutoMapper to handle mapping entity to view model. In cases where you just want to inspect data you don't need to define a view model / DTO, just use an anonymous type. The end result is the same in that EF will execute an optimized query to just return back the data you want rather than entire entities. You can optimize view models to flatten down hierarchical data using this technique. You do need to ensure that any methods or transformations in the .Select() are pure and EF compatible because EF will attempt to translate and pass those to SQL. More complex transformations should be done in the view model itself, or utilize an anonymous type select of the raw data, followed by a ToList/Single/etc. then .Select() into the view model with appropriate transformations via Linq2Object.
One option is to use table splitting which is when you map a single table to two or more entities. The difference with your requested solution is that the "additional" properties in the "full" configuration will be represented by another entity type. Example (for EF Core; EF6 will be very similar):
public class SplitTablePrincipal
{
[Key]
public int Id { get; set; }
public string PrincipalProperty { get; set; }
// principal entity has a nav property to the dependent entity
public virtual SplitTableDependent Dependent { get; set; }
}
public class SplitTableDependent
{
[Key]
public int Id { get; set; }
public string DependentProperty { get; set; }
}
public class SplitTablePricipalConfiguration : IEntityTypeConfiguration<SplitTablePrincipal>
{
public void Configure( EntityTypeBuilder<SplitTablePrincipal> builder )
{
//builder.HasKey( pe => pe.Id );
// establish 1:? relationship w/ shared primary key
builder.HasOne( pe => pe.Dependent )
.WithOne()
.HasForeignKey<SplitTableDependent>( de => de.Id ); // FK is PK
builder.ToTable( "YourTableName" );
}
}
public class SplitTableDependentConfiguration : IEntityTypeConfiguration<SplitTableDependent>
{
public void Configure( EntityTypeBuilder<SplitTableDependent> builder )
{
//builder.HasKey( de => de.Id );
// map dependent entity to same table as principal
builder.ToTable( "YourTableName" ); // same table name
}
}
You only need to include a DbSet for the SplitTablePrincipal entity type in your DbContext. When querying, the Dependent property will not be populated by default (your "lite" configuration); you would need to eager load the property for the "full" data configuration via .Include( stp => stp.Dependent ). You could also lazy load or explicitly load the Dependent property further down the line should you so choose. For example:
dbContext.Entry( principalEntity ).Reference( p => p.Dependent ).Load();

Return type when returning values from multiple tables

I have two Models:
public class StudentModel
{
public int Student_Id {get;set;}
public string Student_Name {get;set;}
public int Class_Id {get;set;}
}
public class ClassModel
{
public int Class_Id {get;set;}
public string Class_Name {get;set;}
}
I want to return values from both the tables. What should my return value be?
public ?? GetStudentClass(StageModel model)
{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["LO"].ConnectionString))
{
string getstudent= $"select * from student s,class c where s.classid = c.classid and s.studentid = #studentid;";
DynamicParameters parameter = new DynamicParameters();
parameter.Add("#studentid", model.Student_Id, DbType.Int64);
connection.Open();
result = connection.Execute(getstudent, parameter);
}
}
I should use the output of the SQL to print, using the model defined.
It seems that you are using Dapper for your work. But you have differences between your properties names and your database fields names. This makes the work with Dapper more difficult, so I recommend to change your properties to match the names in the database fields.
With this changes you can get your classes filled automatically by Dapper
First your StudentModel needs to have an instance of the ClassModel declared in the class
public class StudentModel
{
public int StudentId {get;set;}
public string StudentName {get;set;}
public int ClassId {get;set;}
public ClassModel StudentClass {get;set;}
}
public class ClassModel
{
public int ClassId {get;set;}
public string ClassName {get;set;}
}
now you change the method in this way
public StudentModel GetStudent(StageModel model)
{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["LO"].ConnectionString))
{
string getstudent= #"SELECT s.*, c.ClassId, c.ClassName
FROM student s
INNER JOIN class c on s.classid = c.classid
WHERE s.studentid = #studentid;";
DynamicParameters parameter = new DynamicParameters();
parameter.Add("#studentid", model.Student_Id, DbType.Int64);
connection.Open();
result = connection.Query<StudentModel, ClassModel, StudentModel>
(getstudent, ((s, c) => { s.ClassModel = c; return s;}),
splitOn:"ClassId", parameter);
return result;
}
}
Dapper will create for you the StudentModel instance and the ClassModel instance knowing how to fill the two instances when it meets the ClassId field.
The lambda expression will receive the two instances created by Dapper and you just need to set the ClassModel inside the StudentModel received by the lambda.
This approach follows the impression from your question that between a Student and a Class there is a 1:1 relationship. Of course the matter is more complex if the relationship is 1:N and you need a List inside the StudentModel class

Mongo C# IMongoCollection Design

I am using an interface as a collection type when initializing an IMongoCollection. I am using an interface class as the collection so that it can be better for testing.
public IMongoCollection<IEmployee> Employees => Database.GetCollection<IEmployee>("employee");
public interface IEmployee
{
[BsonId]
ObjectId Id { get; set; }
string Name { get; set; }
}
[BsonDiscriminator(Required = true)]
[BsonKnownTypes(typeof(Employee))]
public class Employee : IEmployee
{
public ObjectId { get; set; }
public string Name { get; set; }
}
I have a database class of Employee which implements IEmployee. When storing to the database, I have to store a type of Employee because I can't declare a new instance of IEmployee.
var emp = new Employee
{
Id = ObjectId.GenerateNewId();
Name = "Wayne Rooney";
};
// Insert into the Employees collection
await Employees.InsertOneAsync(emp);
When I want to replace/update that document, I can't because I am querying from a lower layer class of IEmployee.
await Employees.FindOneAndReplaceAsync(f => f.Id.ToString() == context.Id, g);
context in this case is the parameter of type Employee that I am passing in. g is the update document of type Employee. f in this case is type IEmployee. When I do a replace, I get a [document].Id.ToString() is not supported error.
So the question is, I'm able to insert and retrieve them, but not able to update/replace/delete a document. Any suggestions?
First, a few heads ups.
When inserting to the database, if you have specified an ObjectId as your BsonId then there is no need to manually set it.
So set the BsonId-attribute on your Id.
public class Employee : IEmployee
{
[BsonId]
public ObjectId Id { get; set; }
public string Name { get; set; }
}
And then insert as such:
var emp = new Employee
{
Name = "Wayne Rooney";
};
// Insert into the Employees collection
await Employees.InsertOneAsync(emp);
A unique ObjectId will be set by itself upon inserting.
The problem itself most likely lies in the ToString()-part of your Linq-query. I'm not sure it translates well into a mongo-query. A quick work-around would be to just use something like:
var id = new ObjectId(context.Id);
await Employees.ReplaceOneAsync(f => f.Id == id, g);
This eliminates the ToString()-call and compares ObjectId's directly.
The support of Linq-queries are somewhat limited in the driver. I would advise against using them (there is also some overhead here).
The suggested, and most direct route is to use the built-in filters.
For instance:
var filter = Builders<IEmployee>.Filter
.Eq(nameof(Employee.Id), new ObjectId(context.Id));
await Employees.ReplaceOneAsync(filter, g);
nameof(Employee.Id) is C# 6, and will return "Id". If you do not have support for C# 6 then simply use "Id" instead.

Concat two Ienumerables of different types

I have two instances of IEnumerable<T> (with the Different T). I want to combine both of them .
IEnumerable<ClassA>
IEnumerable<ClassB>
Both in CLass A and ClassB i have one common property .Lets say for example it is EmpId ..
Is there a build-in method in .Net to do that or do I have to write it myself?
Assuming you can extract the common property to a common interface, let's say IEmployee, then you could just Cast() and then Concatenate the collections:
classAItems.Cast<IEmployee>().Concat(classBItems)
Note that this will only iterate over those IEnumerables on demand. If you want to create a List containing the content of both sequences at the time you combined them, you can use ToList():
List<IEmployee> all = classAItems.Cast<IEmployee>().Concat(classBItems).ToList();
You can do the same if you only need an array using ToArray().
You can get the concatenated common property easily enough:
var empIds = first.Select(x => x.EmpId).Concat(second.Select(x => x.EmpId));
If this is not what you are after, you will have to be more specific.
You cannot combine two sequences of different types in one sequence, unless you project some of their properties in a common type and create a sequence of this type.
For instance, let we have the two following classes:
public class A
{
public int ID { get; set; }
public string FirstName { get; set; }
public int Age { get; set; }
}
and
public class B
{
public int ID { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public bool Sex { get; set; }
}
Furthermore, let that you have two sequences, one containing objects of type classA and the other containing objects of type classB. Then, if you declare a third type called classCommon, that would contain the commont properties of classA and classB,
public class classCommon
{
public int ID { get; set; }
public int Age { get; set; }
}
you could try the following:
var result = listA.Select(x => new classCommon { ID = x.ID, Age = x.Age })
.Concat(listB.Select(x => new classCommon{ ID = x.ID, Age = x.Age });
You can concat them with respect to the lowest common denominator, which is object in you case:
IEnumerable<ClassA> e1 = new List<ClassA>();
IEnumerable<ClassB> e2 = new List<ClassB>();
IEnumerable<object> c = e1.Cast<object>()
.Concat(e2.Cast<object>());
But this will not give you much, you will have to runtime check of object type in c collection.
You can create a better common denominator, like some interface IClass which has property EmpId and is implemented by both ClassA and ClassB.
If you do not care about Intellisense, you can try to use dynamic:
IEnumerable<ClassA> e1 = new List<ClassA>() { new ClassA() { A = 1 } };
IEnumerable<ClassB> e2 = new List<ClassB>();
IEnumerable<dynamic> c = e1.Cast<object>()
.Concat(e2.Cast<object>());
int a = c.First().A;
In above code, a will properly result in 1.

Categories