Cast IQueryable generic to another IQueryable generic - c#

I would like to have a common class for my business layers (BL) which has a method convert IQueryable to IQueryable.
- efo: Entity Framework object
- bo: business object
I'm using Entity Framework Core.
public partial class Language //EF object
{
public long Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string Image { get; set; }
public virtual ICollection<News> News { get; set; }
}
public class BOLanguage //Business object
{
public string Name { get; set; }
public string Image { get; set; }
}
public class BL<efo, bo>
where efo : class, new()
where bo : class, new()
{
public Context context;
public BL()
{
context = new Context();
}
public IQueryable<bo> SelectAll()
{
IQueryable<efo> query = context.Set<efo>();
return query.Select(x => new bo
{
//TODO: I don't know how to do here
});;
}
//public IQueryable<BOLanguage> SelectAll() //example
//{
// return context.Language.Select(x => new BOLanguage
// {
// Name = x.Name,
// Image = x.Image
// });
//}
}
Thank you. Hope you understand.

Related

C# Object List Initialization

I have the class below
public class UserClient
{
public int client_id { get; set; }
public Icollection<ClassA> ACollections { get; set; }
public UserClient()
{
ACollections = new Icollection<ClassA>();
}
public class ClassA
{
public string Name { get; set; }
public Icollection<ClassB> BCollections { get; set; }
public ClassA()
{
BCollections = new Icollection<ClassB>();
}
public class ClassB
{
public ClassA ClassA { get; set; }
public Icollection<ClassC> CCollections { get; set; }
public ClassB()
{
CCollections = new Icollection<ClassC>();
}
public class ClassC
{
public ClassB ClassB { get; set;}
}
The three classes fetch and store related data from DB using entity framework and graphql and store the data in an object list that appears on the blazor client in the following JSON format
JSON FORMAT AFTER FETCH
{
public List<UserClient> allData = new List<UserClient>();
// Console.WriteLine(results.Data.ClientData.ToList());
allData = results.Data.ClientData.Select(x => new UserClient
{
client_id = x.Client_id,
ClassA = // <-- this is the point where I Have a problem
}).ToList();
I have difficulties assinging the contents of the second level, third level objects on the list created.

C# REST API - returning an array as JSON

I'm trying to build a REST API. I have been using this guide by Microsoft Docs and I'd appreciate some help.
I have 2 models Library and Book. Each have their own controllers as well.
I want each to reference each other so I can get all books within a library and I want a book to reference what library it belongs to. I am using an in-memory database by Microsoft Entity Framework
My current model classes look like this:
Library:
public class Library
{
[Key]
public long id { get; set; }
public Book[] bookArray { get; set; }
public string postalCode { get; set; }
public string street { get; set; }
public string city { get; set; }
public string country { get; set; }
}
Book:
public class Book
{
[Key]
public long id { get; set; }
public long libraryId { get; set; }
public string title { get; set; }
public string author { get; set; }
public string description { get; set; }
}
I want a GET endpoint like so "api/Libraries/{id}/books" that will return the array of books within a library as JSON, but I can't return the array. I get the error "Can't implicitly convert Models.Book to Microsoft.AspNetCore.Mvc.ActionResult<A2.Models.Library>". Have I setup the model classes correctly? and how do I resolve this error.
The Code:
// GET: api/Libraries/5/books
[HttpGet("{id}/books")]
public async Task<ActionResult<Library>> GetLibraryBooks(long id)
{
var library = await _context.Libraries.FindAsync(id);
if (library == null)
{
return NotFound();
}
return library.bookArray;
}
Your Method should return Book[] like this:
[HttpGet("{id}/books")]
public async Task<ActionResult<Book[]>> GetLibraryBooks(long id)
{
var library = await _context.Libraries.FindAsync(id);
if (library == null)
{
return NotFound();
}
return Ok(library.bookArray);
}
UPDATE
public class Library
{
public Libary(){
books = new List<Book>();
}
[Key]
public long id { get; set; }
public List<Book> books { get; set; }
public string postalCode { get; set; }
public string street { get; set; }
public string city { get; set; }
public string country { get; set; }
}
UPDATE 2
public class LibraryController : Controller
{
private readonly LibraryContext _context;
public LibraryController(LibraryContext context)
{
_context = context;
}
[HttpPost("{id}")]
public IActionResult AddBookToLibrary([FromRoute]long id ,[FromBody] Book bookToAdd)
{
var libraryToAddBook = _context.Libraries.Include(l => l.books)
.FirstOrDefault(l => l.id == id);
if (libraryToAddBook == null)
return NotFound();
libraryToAddBook.books.Add(bookToAdd);
_context.SaveChanges();
return Ok();
}
}
UPDATED CONTEXT
public class LibraryContext : DbContext
{
public LibraryContext(DbContextOptions<LibraryContext> options)
: base(options)
{
}
public DbSet<Library> Libraries { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Library>()
.OwnsMany<Book>(l => l.books);
}
}
startup.cs
var connectionString = Configuration.GetConnectionString("myDatabaseConnectionString");
services.AddDbContext<LibraryContext>(options =>
{
//options.USEYOURDATABASE(connectionString); //you might need install a NuGet eg. Microsoft.EntityFrameworkCore.SqlServer
});

How do I create a generic List using abstract class?

I have a Json class "GetAllDevices()". My JSON response consists of an Array/List of objects, where each object has the below common properties.
public class GetAllDevices
{
[JsonProperty("_id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("actions")]
public Action[] Actions { get; set; }
public class Action
{
public string _id { get; set; }
public Action_Def action_def { get; set; }
}
public class Action_Def
{
public string _id { get; set; }
public string name { get; set; }
}
}
I want to create 2 generic lists containing all the above properties based on its "type".
lstfoo1 List contains all the properties(_id, name type and actions) where type="foo1". Similarly, lstfoo2 is a List which contains the above properties where type="foo2".
What I have done so far:
string strJson=getJSON();
Foo1 lstfoo1=new Foo1();
Foo2 lstfoo2=new Foo2();
List<Foo1> foo1list= lstfoo1.GetDeviceData(strJson);
List<Foo2> foo2list = lstfoo2.GetDeviceData(strJson);
public class AllFoo1: GetAllDevices
{
}
public class AllFoo2: GetAllDevices
{
}
public abstract class HomeDevices<T>
{
public string type { get; set; }
public string _id { get; set; }
public List<AllFoo1> lstfoo1{ get; set; }
public List<AllFoo2> lstfoo2{ get; set; }
public abstract List<T> GetDeviceData(string jsonResult);
}
public class Foo1: HomeDevices<AllFoo1>
{
public Foo1()
{
type = "foo1";
}
public override List<AllFoo1> GetDeviceData(string jsonResult)
{
var lst =Newtonsoft.Json.JsonConvert.DeserializeObject<List<AllFoo1>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
public class Foo2: HomeDevices<AllFoo2>
{
public Foo2()
{
type = "foo2";
}
public override List<AllFoo2> GetDeviceData(string jsonResult)
{
var lst = Newtonsoft.Json.JsonConvert.DeserializeObject<List<AllFoo2>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
My question is, is there an easier way to do this using abstract classes? Can I directly convert my "GetAllDevices" class into an abstract class and inherit it and deserialize into it and create a generic list?
This should help, if I understand your problem correctly. Let me know if you have questions or it doesn't work as you need. I put this together really quickly without testing.
The way the Type property is defined could be improved but I left it as you had it.
public class MyApplication
{
public void DoWork()
{
string json = getJSON();
DeviceTypeOne foo1 = new DeviceTypeOne();
DeviceTypeTwo foo2 = new DeviceTypeTwo();
IList<DeviceTypeOne> foo1Results = foo1.GetDeviceData(json); // calls GetDeviceData extension method
IList<DeviceTypeTwo> foo2Results = foo2.GetDeviceData(json); // calls GetDeviceData extension method
}
}
// implemented GetDeviceData as extension method of DeviceBase, instead of the abstract method within DeviceBase,
// it's slightly cleaner than the abstract method
public static class DeviceExtensions
{
public static IList<T> GetDeviceData<T>(this T device, string jsonResult) where T : DeviceBase
{
IEnumerable<T> deviceDataList = JsonConvert.DeserializeObject<IEnumerable<T>>(jsonResult);
IEnumerable<T> resultList = deviceDataList.Where(x => x.Type.Equals(typeof(T).Name));
return resultList.ToList();
}
}
// abstract base class only used to house common properties and control Type assignment
public abstract class DeviceBase : IDeviceData
{
protected DeviceBase(string type)
{
if(string.IsNullOrEmpty(type)) { throw new ArgumentNullException(nameof(type));}
Type = type; // type's value can only be set by classes that inherit and must be set at construction time
}
[JsonProperty("_id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public string Type { get; private set;}
[JsonProperty("actions")]
public DeviceAction[] Actions { get; set; }
}
public class DeviceTypeOne : DeviceBase
{
public DeviceTypeOne() : base(nameof(DeviceTypeOne))
{
}
}
public class DeviceTypeTwo : DeviceBase
{
public DeviceTypeTwo() : base(nameof(DeviceTypeTwo))
{
}
}
// implemented GetAllDevices class as IDeviceData interface
public interface IDeviceData
{
string Id { get; set; }
string Name { get; set; }
string Type { get; }
DeviceAction[] Actions { get; set; }
}
// renamed and relocated class Action to DeviceAction
public class DeviceAction
{
public string Id { get; set; }
public DeviceActionDefinition DeviceActionDefinition { get; set; }
}
// renamed and relocated Action_Def to DeviceActionDefinition
public class DeviceActionDefinition
{
public string Id { get; set; }
public string Name { get; set; }
}
It should be simple enough to move the implementation of method GetDeviceData() to the base class.
For this to work, you will need to add a constraint on T so the compiler knows a bit more about the base type. You will also need to implement a constructor to populate the concrete type's type string you use around. This is a necessary measure to ensure the value is always populated as it is used for comparison in the method in question:
public abstract class HomeDevices<T> where T: GetAllDevices
{
public HomeDevices(string concreteType)
{
type = concreteType;
}
public string type { get; set; }
public string _id { get; set; }
public List<AllFoo1> lstfoo1 { get; set; }
public List<AllFoo2> lstfoo2 { get; set; }
//This method is now generic and works for both.
public List<T> GetDeviceData(string jsonResult)
{
var lst = Newtonsoft.Json.JsonConvert.DeserializeObject<List<T>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
I hope that helps.

How to create complex mapping using AutoMapper?

I have the following Entity-Models
public class Blog
{
public int Id { get; set;}
public string Title { get; set; }
public string Body { get; set; }
[ForeignKey("Category")]
public int? CategoryId { get; set; }
public virtual Category Category { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Category
{
public int Id { get; set;}
public string Name { get; set; }
}
public class Comment
{
public int Id { get; set;}
public string Title { get; set; }
public string Body { get; set; }
[ForeignKey("Blog")]
public int BlogId { get; set; }
public virtual Blog Blog { get; set; }
}
Then I have the following view-model in which I like to tell AutoMapper to map the Blog object into the BlogViewModel notice the CategoryName property will need to come from Blog.Category.Name and each Comment in the Blog.Comments need to be converter to CommentViewModel using the organic convention.
I currently set the mapping at run time using reflection for any class that implements the ICustomMap interface. Please read the comment in the code over the Transfer(IMapper mapper) method.
public class BlogViewModel : ICustomMapFrom
{
public int Id { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public string MyCatName { get; set; }
public IEnumerable<CommentViewModel> Comments { get; set; }
// **IMPORTANT NOTE**
// This method is called using reflection when the on Application_Start() method.
// If IMapper is the wrong Interface to pass, I can change
// the implementation of ICustomMap
// I assumed that `IMapper` is what is needed to add configuration at runtime.
public void Transfer(IConfigurationProvider config)
{
// How to I do the custom mapping for my MyCatName and Comments?
// I need to use the config to create the complex mapping
// AutoMapper.Mapper.Map(typeof(Blog), typeof(BlogViewModel));
}
}
Finally here is my CommentViewModel
public class CommentViewModel : IMapFrom<Comment>
{
public int Id { get; set; }
public string Title { get; set; }
public string Body { get; set; }
}
How can I tell AutoMapper how to map the CategoryName and the Comments?
Updated
Here is how I would create the mapping. I would have the following 3 interfaces
public interface IMap
{
}
public interface IMapFrom<T> : IMap
{
}
public interface ICustomMapFrom : IMap
{
void Map(IConfigurationProvider config);
}
Then in the Global.cs file
I would execute the Run method on startup. Basically this method will scan assemblies and register the classes that I would want to register using the interfaces.
public class ConfigureAutoMapper
{
public void Run()
{
var types = AssemblyHelpers.GetInternalAssemblies()
.SelectMany(x => x.GetTypes())
.Where(x => x.IsClass && !x.IsAbstract && !x.IsInterface && typeof(IMap).IsAssignableFrom(x))
.ToList();
RegisterStandardMappings(types);
RegisterCustomMappings(types);
}
private static void RegisterStandardMappings(IEnumerable<Type> types)
{
foreach (Type type in types)
{
if(type.IsGenericType && typeof(IMapFrom<>).IsAssignableFrom(type))
{
AutoMapper.Mapper.Map(type.GetGenericArguments()[0], type);
}
}
}
private static void RegisterCustomMappings(IEnumerable<Type> types)
{
foreach (Type type in types)
{
if (typeof(ICustomMapFrom).IsAssignableFrom(type))
{
ICustomMapFrom map = (ICustomMapFrom)Activator.CreateInstance(type);
var t = AutoMapper.Mapper.Configuration;
map.Map(Mapper.Configuration);
}
}
}
}
I wrote an NUnit test which sets up AutoMapper with your classes. AutoMapper supports the mapping of CategoryName out of the box.
[TestFixture]
public class TestClass
{
[Test]
public void Test1()
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Blog, BlogViewModel>();
});
config.AssertConfigurationIsValid();
var blog = new Blog()
{
Body = "Blog body",
Category = new Category { Name = "My Category" },
Comments = new List<Comment>() {
new Comment { Body = "Comment body 1" },
new Comment { Body = "Comment body 2" }
}
};
var mapper = config.CreateMapper();
var result = mapper.Map<Blog, BlogViewModel>(blog);
Assert.AreEqual(blog.Body, "Blog body");
Assert.AreEqual(blog.Category.Name, result.CategoryName);
List<CommentViewModel> comments = result.Comments.ToList();
Assert.That(comments.Any(c => c.Body == "Comment body 1"));
Assert.That(comments.Any(c => c.Body == "Comment body 2"));
}
}

Reusing common mapping code across common DTO classes

I have 2 DTO classes that have multiple common properties, I'm trying to avoid having to repeat myself when writing mapping code for entity to DTO conversion, I'm wondering how I could achieve this, I have a feeling I need to probably use a Func or Action delegate to achieve this. For example I have 2 classes StudentDTO and EmployeeDTO:
public class StudentDTO : PersonDTO
{
public int CourseId { get; set; }
//other properties
}
public class EmployeeDTO : PersonDTO
{
public int OccupationId { get; set; }
//other properties
}
and both naturally inherit from PersonDTO:
public class PersonDTO
{
public int Id { get; set; }
public string FirstName { get; set; }
public string FamilyName { get; set; }
public int Age { get; set; }
}
How could I reuse the mapping code that maps the common properties? Thanks.
You probably can do something like this (very basic and not elegant):
(note Entity can off course be a DataReader, DataSet etc.)
public class Entity
{
public string FirstName { get; set; }
public string FamilyName { get; set; }
public int CourseId { get; set; }
public int OccupationId { get; set; }
}
public class BaseDto
{
}
public class PersonDto : BaseDto
{
public string FirstName { get; set; }
public string FamilyName { get; set; }
public static void Map(Entity entity, PersonDto personDto)
{
personDto.FirstName = entity.FirstName;
personDto.FamilyName = entity.FamilyName;
}
}
public class StudentDto : PersonDto
{
public int CourseId { get; set; }
public static StudentDto Map(Entity entity)
{
var studentDto = new StudentDto { CourseId = entity.CourseId };
// ..can call map to PersonDto if you want
return studentDto;
}
}
public class EmployeeDto : PersonDto
{
public int OccupationId { get; set; }
public static EmployeeDto Map(Entity entity)
{
var employeeDto = new EmployeeDto() { OccupationId = entity.OccupationId };
// ..can call map to PersonDto if you want
return employeeDto;
}
}
public class Mapper<TDto>
where TDto : BaseDto
{
private TDto _dto;
private readonly Entity _entity;
public Mapper(Entity entity)
{
_entity = entity;
}
public Mapper<TDto> Map(Func<Entity, TDto> map)
{
_dto = map(_entity);
return this;
}
public Mapper<TDto> Map<TBaseDto>(Action<Entity, TBaseDto> map)
where TBaseDto : BaseDto
{
map(_entity, _dto as TBaseDto);
return this;
}
public TDto Result
{
get { return _dto; }
}
}
class Program
{
static void Main(string[] args)
{
var studentEntity = new Entity() { FirstName = "John", FamilyName = "Doe", CourseId = 1 };
var studentDto = new Mapper<StudentDto>(studentEntity)
.Map(StudentDto.Map)
.Map<PersonDto>(PersonDto.Map)
.Result;
}
}
Use a library.. that's what they are there for!
Automapper
ValueInjecter
In Automapper, your above mapping becomes incredibly simple:
Mapper.CreateMap<EmployeeDTO, StudentDTO>();
Mapper.CreateMap<StudentDTO, EmployeeDTO>();
..then when you want to map:
var studentInstance = ...; // go get student instance
var employee = Mapper.Map<Employee>(studentInstance);

Categories

Resources