How to keep the value of the source when using InjectFrom - asp.net-mvc-3

By injecting values ​​into my domain object, I would keep the values ​​of some properties.
Example:
Domain model
public class Person
{
public string Name { get; set; }
public Guid ID { get; set; }
public DateTime CreateAt { get; set; }
public string Notes { get; set; }
public IList<string> Tags { get; set; }
}
View Model
public class PersonViewMode
{
public string Name { get; set; }
public Guid ID { get; set; }
public DateTime CreateAt { get; set; }
public string Notes { get; set; }
public IList<string> Tags { get; set; }
public PersonViewMode() { ID = Guid.NewGuid(); } //You should use this value when it is the Target
}
Sample
var p = new Person
{
ID = Guid.NewGuid() //Should be ignored!
,
Name = "Riderman"
,
CreateAt = DateTime.Now
,
Notes = "teste de nota"
,
Tags = new[] {"Tag1", "Tag2", "Tag3"}
};
var pvm = new PersonViewMode();
pvm.InjectFrom(p); //Should use the ID value generated in the class constructor PersonViewMode

if you delete the set; from from the ViewModel's ID then it won't be set;
otherwise you could save the value of ID in a separate variable and put it back after injecting,
or you can create a custom valueinjection that would ignore "ID" or would receive a list of properties to ignore as a parameter
here's the example for a custom injection that receives a list of property names to ignore:
public class MyInj : ConventionInjection
{
private readonly string[] ignores = new string[] { };
public MyInj(params string[] ignores)
{
this.ignores = ignores;
}
protected override bool Match(ConventionInfo c)
{
if (ignores.Contains(c.SourceProp.Name)) return false;
return c.SourceProp.Name == c.TargetProp.Name && c.SourceProp.Type == c.TargetProp.Type;
}
}
and use it like this:
pvm.InjectFrom(new MyInj("ID"), p);
if you need to ignore more, you can do like this:
pvm.InjectFrom(new MyInj("ID","Prop2","Prop3"), p);

Related

Fluent LINQ EF Core - Select filtered child property

I have the classes below:
public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class ParentEntity
{
public Guid Id { get; set; }
public string SomeProperty { get; set; }
public ICollection<ChildEntity> ChildEntities { get; set; }
}
public class ChildEntity
{
public Guid Id { get; set; }
public int Vote { get; set; }
public Guid UserId { get; set; }
}
public class ReturnedParentDto
{
public Guid Id { get; set; }
public string SomeProperty { get; set; }
public int Vote { get; set; }
}
I want to be able to return a full list of ParenEntities, but take an Id of the User class (UserClassId), then filter the ParentEntity's ICollection where UserUid = UserClassId, so only 1 ChildEntity is always returned. Then I would want to extract a specific field from that returned ChildEntity and merge it with the ParentEntity fields. The end result should be like the ReturnedParentDto.
I want to do it in the style like
ParentEntities.Include(v => v.ChildEntities).ToList()
That seems to be possible in EF Core 5, but my project is in 3.1.
You can do this as below
Approach 1:
var result = result = parentEntities.Include(x => x.ChildEntities.Where(y => y.UserId == userId))
.Select(x => new ReturnedParentDto {
Id = x.Id,
SomeProperty = x.SomeProperty,
Vote = x.ChildEntities.FirstOrDefault()?.Vote // userId is the variable here
});
Approach 2:
var result = parentEntities.Select(x =>
new ReturnedParentDto {
Id = x.Id,
SomeProperty = x.SomeProperty,
Vote = x.ChildEntities.FirstOrDefault(y => y.UserId == userId)?.Vote // userId is the variable here
});

I try to add entity model class data to my another list ,but after foreach Same rows insert in every row , MVC

This is my Entity model class which was auto generated by Ado.net model
public partial class SubModule
{
public int SubModuleId { get; set; }
public Nullable<int> ModuleId { get; set; }
public string SubModuleName { get; set; }
public Nullable<bool> Active { get; set; }
public Nullable<bool> IsModules { get; set; }
public string url { get; set; }
public string path { get; set; }
public string subform { get; set; }
}
this is my another class
public class ChildModules
{
public int ? SubModuleId { get; set; }
public Nullable<int> ModuleId { get; set; }
public string SubModuleName { get; set; }
public Nullable<bool> Active { get; set; }
public Nullable<bool> IsModules { get; set; }
public string url { get; set; }
public string path { get; set; }
public string subform { get; set; }
}
I want to copy Sub modules data to my Child modules class properties
My code is
List<SubModule> ChildModule = entity.SubModules.Where(x => x.IsModules == false).ToList();
List<ChildModules> listchildmodules = new List<ChildModules>();
ChildModules chmodule = new ChildModules();
foreach (SubModule mod in ChildModule)
{
chmodule.SubModuleId = mod.SubModuleId;
chmodule.ModuleId = mod.ModuleId;
chmodule.SubModuleName = mod.SubModuleName;
chmodule.Active = mod.Active;
chmodule.IsModules = mod.IsModules;
chmodule.url = mod.url;
chmodule.path = mod.path;
chmodule.subform = mod.subform;
listchildmodules.Add(chmodule);
}
but in listchildmodules last row insert in every index.
Why?
Your code always add the same object always. Because you always updating the values of same object and insert that into list.
Keep the below line of code inside foreach.
ChildModules chmodule = new ChildModules();
Your foreach should look like below
foreach (SubModule mod in ChildModule)
{
ChildModules chmodule = new ChildModules();
chmodule.SubModuleId = mod.SubModuleId;
chmodule.ModuleId = mod.ModuleId;
chmodule.SubModuleName = mod.SubModuleName;
chmodule.Active = mod.Active;
chmodule.IsModules = mod.IsModules;
chmodule.url = mod.url;
chmodule.path = mod.path;
chmodule.subform = mod.subform;
listchildmodules.Add(chmodule);
}
Or you could declare ChildModules chmodule; outside foreach and initialize chmodule = new ChildModules(); inside foreach loop.

Convert collections of database entities to collections of view models

I am working on a .NET Core Web API
So far I used to return anonymous types in my controllers but now I want to start using the full power of swagger with auto documentation of the return types.
Which lead me to start using view models.
But I am struggling with converting between the auto-generated database model classes
and the auto-generated swagger view model classes.
It works for a single instance (see GetPerson method in the controller below) but fails when I want to return lists.
So my questions:
How do I cast/convert collections/lists of objects between view models and database models
Is the code in the controller correct? Are there easier/shorter/better ways to do the conversion? (I read about using the implicit operator)
Error message I get:
Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Collections.Generic.List'. An explicit conversion exists (are you missing a cast?)
It gives me an InvalidCastException if I cast them explicitly like
List result = (List)_dbContext.Person....
there seems to be a problem with generics in the display of stackoverflow
Assume I used the generic lists with giving a type PersonView
My code looks like:
Database models
public partial class Person
{
public Person()
{
}
public int Id { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public int? MainAdressId { get; set; }
public virtual Adress MainAdress { get; set; }
}
public partial class Adress
{
public Adress()
{
Person = new HashSet();
}
public int Id { get; set; }
public string CityName { get; set; }
public int CityPostalCode { get; set; }
public string StreetName { get; set; }
public string HouseNumber { get; set; }
public string FloorNumber { get; set; }
public string DoorNumber { get; set; }
public virtual ICollection Person { get; set; }
}
View models
public class City
{
public string Name { get; set; }
public int PostalCode { get; set; }
}
public class Street
{
public string Name { get; set; }
public string HouseNumber { get; set; }
public string FloorNumber { get; set; }
public string DoorNumber { get; set; }
}
public class AdressView
{
public Street Street { get; set; }
public City City { get; set; }
}
public class PersonView
{
public string FirstName { get; set; }
public string Lastname { get; set; }
public AdressView Adress { get; set; }
}
The controller class which is working for a single instance but not for lists
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Swashbuckle.SwaggerGen.Annotations;
using PersonExample.ModelsPersonDB;
using PersonExample.ModelsViewPerson;
namespace PersonExample.Controllers
{
[Route("api/[controller]")]
public class PersonViewTestController : Controller
{
private readonly PersonDBContext _dbContext;
private readonly ILogger _logger;
public PersonViewTestController(PersonDBContext dbContext, ILogger logger)
{
_dbContext = dbContext;
_logger = logger;
_logger.LogDebug("{0} > new instance created", GetType().Name);
}
[HttpGet("{id:int}", Name = "GetPerson")]
[ProducesResponseType(typeof(PersonView), 200)]
[SwaggerOperation("GetPerson")]
public virtual IActionResult GetPerson([FromRoute]int id)
{
PersonView result = _dbContext.Person
.Include(p => p.MainAdress)
.Where(p => p.Id == id)
.Select(p => new PersonView()
{
FirstName = p.Firstname,
Lastname = p.Lastname,
Adress = (p.MainAdress == null) ? null :
new AdressView()
{
Street = new Street()
{
Name = p.MainAdress.StreetName,
HouseNumber = p.MainAdress.HouseNumber,
FloorNumber = p.MainAdress.FloorNumber,
DoorNumber = p.MainAdress.DoorNumber
},
City = new City()
{
Name = p.MainAdress.CityName,
PostalCode = p.MainAdress.CityPostalCode
}
}
}
)
.FirstOrDefault();
return new ObjectResult(result);
}
[HttpGet(Name = "GetPersonList")]
[ProducesResponseType(typeof(List), 200)]
[SwaggerOperation("GetPersonList")]
public virtual IActionResult GetPersonList()
{
List result = _dbContext.Person
.Include(p => p.MainAdress)
.Select(p => new PersonView()
{
FirstName = p.Firstname,
Lastname = p.Lastname,
Adress = (p.MainAdress == null) ? null :
new AdressView()
{
Street = new Street()
{
Name = p.MainAdress.StreetName,
HouseNumber = p.MainAdress.HouseNumber,
FloorNumber = p.MainAdress.FloorNumber,
DoorNumber = p.MainAdress.DoorNumber
},
City = new City()
{
Name = p.MainAdress.CityName,
PostalCode = p.MainAdress.CityPostalCode
}
}
}
);
return new ObjectResult(result);
}
}
}
you can use AutoMapper https://github.com/AutoMapper/AutoMapper/wiki/Getting-started
here some examples: Simple Automapper Example
example with EF core and ASP.NET WebApi: https://github.com/chsakell/aspnet5-angular2-typescript
I missed the .ToList() at the end of the query.
The full controller know looks like:
[HttpGet(Name = "GetPersonList")]
[ProducesResponseType(typeof(List), 200)]
[SwaggerOperation("GetPersonList")]
public virtual IActionResult GetPersonList()
{
List result = _dbContext.Person
.Include(p => p.MainAdress)
.Select(p => new PersonView()
{
FirstName = p.Firstname,
Lastname = p.Lastname,
Adress = (p.MainAdress == null) ? null :
new AdressView()
{
Street = new Street()
{
Name = p.MainAdress.StreetName,
HouseNumber = p.MainAdress.HouseNumber,
FloorNumber = p.MainAdress.FloorNumber,
DoorNumber = p.MainAdress.DoorNumber
},
City = new City()
{
Name = p.MainAdress.CityName,
PostalCode = p.MainAdress.CityPostalCode
}
}
}
).ToList(); //missed that line
return new ObjectResult(result);
}

Joining in LINQ to select a sublist within a list

I have two classes as follows:
public class HRM_SERVICE_PERD_BNFT_DTLModel
{
public string SRVC_PERD_BNFT_CODE { get; set; }
public string RTR_BNFT_CODE { get; set; }
public string RTR_BNFT_NAME { get; set; }
public string RTR_BNFT_CALC_MODE { get; set; }
public string SAL_HEAD_CODE { get; set; }
public string SAL_HEAD_NAME { get; set; }
public string RTR_BNFT_IN_PERCENT { get; set; }
public string RTR_BNFT_AMOUNT { get; set; }
public string RTR_BNFT_INACTV_DATE { get; set; }
public short? RTR_BNFT_SLNO { get; set; }
}
public class HRM_RETIREMENT_BENEFITModel : BaseModel
{
public string RTR_BNFT_CODE { get; set; }
public string RTR_BNFT_NAME { get; set; }
public string RTR_BNFT_SRTNM { get; set; }
public string RTR_BNFT_REM { get; set; }
public short? RTR_BNFT_SLNO { get; set; }
}
Now I want to select data from the first model for a specific 'SRVC_PERD_BNFT_CODE', then I need to select the 'RTR_BNFT_NAME' from the second table for all the filtered data along with some other value from the first model. I'm trying kind of joining, but not getting the name. It's showing system.collection.string instead of value.
Here is what I'm trying so far:
public List<HRM_SERVICE_PERD_BNFT_DTLModel> GetBenefitData(string mstcode)
{
var model2 = DataContext.HRM_SERVICE_PERD_BNFT_DTL.AsEnumerable().Where(m => m.SRVC_PERD_BNFT_CODE == mstcode).Select(s=>s).ToList();
var model = DataContext.HRM_SERVICE_PERD_BNFT_DTL.AsEnumerable().Where(m => m.SRVC_PERD_BNFT_CODE == mstcode)
.Select(s => new HRM_SERVICE_PERD_BNFT_DTLModel
{
RTR_BNFT_CODE = s.RTR_BNFT_CODE,
RTR_BNFT_SLNO = s.RTR_BNFT_SLNO,
RTR_BNFT_CALC_MODE = s.RTR_BNFT_CALC_MODE,
RTR_BNFT_AMOUNT = (s.RTR_BNFT_AMOUNT).ToString(),
RTR_BNFT_NAME = (from x in model2
join c in DataContext.HRM_RETIREMENT_BENEFIT on x.RTR_BNFT_CODE equals c.RTR_BNFT_CODE into p
from b in p.AsEnumerable()
select b.RTR_BNFT_NAME).ToList().ToString(),
RTR_BNFT_IN_PERCENT = (s.RTR_BNFT_IN_PERCENT).ToString()
}).ToList();
return model;
}
What I'm doing wrong? Please help.
Following is suspicious.
.ToList().ToString()
Remove the ToString part and replace it with something like FirstorDefault, then it should work.
Reason for getting something like System.Collection.String is that List<T>.ToString() is usually typename until it is overridden in some way.
Following minimal snipped produce System.Collections.Generic.List1[System.String]`
List<String> list = new List<String>();
var str = list.ToString();
Console.WriteLine(str); //System.Collections.Generic.List`1 [ System.String ]

most efficient Entity Framework Code First method of flattening / projecting parent entity with specific child

I have a parent entity Widget with core members and multiple WidgetTranslation children that have language translated members i.e. Description text available in English, French, German etc.
e.g.
public class Widget
{
public int Id { get; set; }
public string Code { get; set; }
public virtual ICollection<WidgetTranslation> WidgetTranslations { get; set; }
}
public class WidgetTranslation
{
public int WidgetId { get; set; }
public virtual Widget Widget { get; set; }
public int LanguageId { get; set; }
public virtual Language Language { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Summary { get; set; }
}
What is the most efficient method of querying the widget collection, flattening for a given LanguageId & projecting to a TranslatedWidget DTO
public class TranslatedWidget
{
public int Id { get; set; }
public string Code { get; set; }
public int LanguageId { get; set; }
public virtual Language Language { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Summary { get; set; }
}
Given languageId I've started with
DbSet.Select(w => new TranslatedWidget
{
Id = w.Id,
Code = w.Code,
LanguageId = w.LanguageId,
Name = w.WidgetTranslations.First(wt=>wt.LanguageId == languageId).Name,
Description = w.WidgetTranslations.First(wt=>wt.LanguageId == languageId).Description,
Summary = w.WidgetTranslations.First(wt=>wt.LanguageId == languageId).Summary
});
But I've a feeling this is inefficient and won't scale for more properties on WidgetTranslation.
Thanks
Use SelectMany to flatten structures via a single join:
var widgetQuery = from w in dbSet.Widgets
from wt in w.WidgetTranslations
where wt.Language == languageId
select new TranslatedWidget
{
Id = w.Id,
Code = w.Code,
LanguageId = w.LanguageId,
Name = wt.Name,
Description = wt.Description,
Summary = wt.Summary
});
I'm assuming here that you only have a single translation for each widget in a given language.
I would move Name, Description and Summary into a nested class of your DTO...
public class TranslatedWidgetTranslation
{
public string Name { get; set; }
public string Description { get; set; }
public string Summary { get; set; }
}
public class TranslatedWidget
{
public int Id { get; set; }
public string Code { get; set; }
public int LanguageId { get; set; }
public TranslatedWidgetTranslation Translation { get; set; }
}
Then you can project into that class and need First only once which would result in only one TOP(1) subquery in SQL instead of three:
DbSet.Select(w => new TranslatedWidget
{
Id = w.Id,
Code = w.Code,
LanguageId = languageId,
Translation = w.WidgetTranslations
.Where(wt => wt.LanguageId == languageId)
.Select(wt => new TranslatedWidgetTranslation
{
Name = wt.Name,
Description = wt.Description,
Summary = wt.Summary
})
.FirstOrDefault()
});
You must use FirstOrDefault here, First is not supported in a LINQ-to-Entities projection.
If you don't want that nested type you can project into anonymous types first and then convert into your final class, but the code will be a bit longer:
DbSet.Select(w => new
{
Id = w.Id,
Code = w.Code,
LanguageId = languageId,
Translation = w.WidgetTranslations
.Where(wt => wt.LanguageId == languageId)
.Select(wt => new
{
Name = wt.Name,
Description = wt.Description,
Summary = wt.Summary
})
.FirstOrDefault()
})
.AsEnumerable()
.Select(x => new TranslatedWidget
{
Id = x.Id,
Code = x.Code,
LanguageId = x.LanguageId,
Name = x.Translation != null ? x.Translation.Name : null,
Description = x.Translation != null ? x.Translation.Description : null,
Summary = x.Translation != null ? x.Translation.Summary : null
});

Resources