Entity to View Model Serialize in Framework Serialization - c#

I'm creating a view model for a Customer entity in EF. My question is if I'm using the correct approach. I convert an entity property to a view model property. And, if what I need to return is a list I convert the properties for each object.
Is there a better way to do this serialization? I'm not asking about if convert an entity to a model is the correct approach. I'm not doing that, I just return what is needed. What I want to know is if is there a better way to serialize the entity to an object in a view model.
This is my view model:
public class CustomerModel
{
public int TotalRecords { get; set; }
public int CUSTOMER_KEY { get; set; }
public decimal CCUSTID { get; set; }
public string CCNAME { get; set; }
public string ACCNOTES { get; set; }
public string CUSTPRCLEVEL_CODE { get; set; }
public string CUSTPRCLEVEL_CODE_Name { get; set; }
public DateTime LASTMODIFIEDDATE { get; set; }
public string LASTMODIFIEDBY { get; set; }
public static Customer FromModelToEntity(CustomerModel model)
{
Customer entity = new Customer();
entity.CUSTOMER_KEY = model.CUSTOMER_KEY;
entity.CCUSTID = model.CCUSTID;
entity.CCNAME = model.CCNAME != null ? model.CCNAME : null;
entity.ACCNOTES = model.ACCNOTES != null ? model.ACCNOTES : null;
entity.CUSTPRCLEVEL_CODE = model.CUSTPRCLEVEL_CODE != null ? model.CUSTPRCLEVEL_CODE : null;
entity.LASTMODIFIEDDATE = model.LASTMODIFIEDDATE;
entity.LASTMODIFIEDBY = model.LASTMODIFIEDBY != null ? model.LASTMODIFIEDBY : null;
return entity;
}
public static CustomerModel FromEntityToModel(Customer entity)
{
CustomerModel model = new CustomerModel();
model.CUSTOMER_KEY = entity.CUSTOMER_KEY;
model.CCUSTID = entity.CCUSTID;
model.CCNAME = entity.CCNAME != null ? entity.CCNAME : null;
model.ACCNOTES = entity.ACCNOTES != null ? entity.ACCNOTES : null;
model.CUSTPRCLEVEL_CODE = entity.CUSTPRCLEVEL_CODE != null ? entity.CUSTPRCLEVEL_CODE : null;
model.CUSTPRCLEVEL_CODE_Name = entity.CustomerPricingLevel != null ? entity.CustomerPricingLevel.DESCRIPTION : string.Empty;
model.LASTMODIFIEDDATE = entity.LASTMODIFIEDDATE;
model.LASTMODIFIEDBY = entity.LASTMODIFIEDBY != null ? entity.LASTMODIFIEDBY : null;
return model;
}
public static List<Customer> FromModelToEntity(List<CustomerModel> modelList)
{
List<Customer> entityList = new List<Customer>();
foreach (var item in modelList)
{
entityList.Add(CustomerModel.FromModelToEntity(item));
}
return entityList;
}
public static List<CustomerModel> FromEntityToModel(List<Customer> entityList)
{
List<CustomerModel> modelList = new List<CustomerModel>();
foreach (var item in entityList)
{
modelList.Add(CustomerModel.FromEntityToModel(item));
}
return modelList;
}
}

Not sure what sort of answer you are seeking.
You can shorten your code using LINQ though. (System.Linq)
Insead of the foreach, you can use a 1 liner:
So that this:
public static List<Customer> FromModelToEntity(List<CustomerModel> modelList)
{
List<Customer> entityList = new List<Customer>();
foreach (var item in modelList)
{
entityList.Add(CustomerModel.FromModelToEntity(item));
}
return entityList;
}
Turns into this:
public static List<Customer> FromModelToEntity(List<CustomerModel> modelList)
{
return modelList.Select(item => FromModelToEntity(item)).ToList();
}

Related

Include all navigation properties with recurcive properties EF Core

I have tried to Eagerly load all the data related to an entity, but I still have a problem regarding the recusive properties like this one :
public class Node : BaseAbstractEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
[ForeignKey("TypeId")]
public virtual NodeType Type { get; set; }
public int? TypeId { get; set; }
[ForeignKey("ParentId")]
public virtual Node Parent { get; set; }
public int? ParentId { get; set; }
public ICollection<Node> Children { get; set; }
}
I am using it in this method :
public async Task<object> JustGetAsync(Type type, JObject value, DbContext context)
{
int id = 0;
if (value != null && value["id"] != null)
id = Int32.Parse(value["id"].ToString());
if (id != 0)
return await context.FindAsync(type, id);
var TypeSet = (IQueryable<object>) context.GetType()
.GetMethod("Set")
.MakeGenericMethod(type)
.Invoke(context, null);
return await TypeSet.Include(context.GetIncludePaths(type)).ToListAsync();
}
the get IncludePaths is a code that I found here enter link description here that helps to eager all the properties :
public static IQueryable Include(this IQueryable source, IEnumerable navigationPropertyPaths)
where T : class
{
return navigationPropertyPaths.Aggregate(source, (query, path) => query.Include(path));
}
public static IEnumerable<string> GetIncludePaths(this DbContext context, Type clrEntityType)
{
var entityType = context.Model.FindEntityType(clrEntityType);
var includedNavigations = new HashSet<INavigation>();
var stack = new Stack<IEnumerator<INavigation>>();
while (true)
{
var entityNavigations = new List<INavigation>();
foreach (var navigation in entityType.GetNavigations())
{
if (includedNavigations.Add(navigation))
entityNavigations.Add(navigation);
}
if (entityNavigations.Count == 0)
{
if (stack.Count > 0)
yield return string.Join(".", stack.Reverse().Select(e => e.Current.Name));
}
else
{
foreach (var navigation in entityNavigations)
{
var inverseNavigation = navigation.FindInverse();
if (inverseNavigation != null)
includedNavigations.Add(inverseNavigation);
}
stack.Push(entityNavigations.GetEnumerator());
}
while (stack.Count > 0 && !stack.Peek().MoveNext())
stack.Pop();
if (stack.Count == 0) break;
entityType = stack.Peek().Current.GetTargetType();
}
}

cannot convert from 'System.Collections.Generic.ICollection<x>' to 'x'

i need to add some data in OptionRoleTable:
public class OptionRole
{
public int Id { get; set; }
public int RoleId { get; set; }
public int OptionsId { get; set; }
public virtual Role Role { get; set; }
public virtual Options Options { get; set; }
}
and this is Options Tabel:
public partial class Options
{
public int Id { get; set; }
public string OptionName { get; set; }
public string RouteFunctionName { get; set; }
public string Icon { get; set; }
public virtual ICollection<OptionRole> OptionRoles { get; set; }
}
i must check data not exist in OptionRole , when i using this code for add data in OptionRole :
public async Task<Options> findOptionsId(int optionId)
{
return await _option.FirstOrDefaultAsync(x => x.Id == optionId);
}
public async Task<bool> AddorUpdateOptions(int optionId, IList<int> selectedRoleValue)
{
List<OptionVM> optionVMs = new List<OptionVM>();
List<int> currentOptionValue = new List<int>();
var optionRole = await findOptionsId(optionId);
if (optionRole == null)
{
return false;
}
foreach (var item in selectedRoleValue)
{
var findRole = await _roleManager.FindByIdAsync(item);
var findOPR = optionRole.OptionRoles.FirstOrDefault(x => x.OptionsId== optionId && x.RoleId==item);
if (findOPR != null)
{
currentOptionValue.Add(item);
}
}
if (selectedRoleValue == null)
{
selectedRoleValue = new List<int>();
}
var newOptionRole = selectedRoleValue.Except(currentOptionValue).ToList();
foreach (var opRole in newOptionRole)
{
var findRole = await _roleManager.FindByIdAsync(opRole);
if (findRole != null)
{
optionRole.OptionRoles.Add(new OptionRole
{
OptionsId = optionRole.Id,
RoleId = findRole.Id
});
}
}
var removeOptionRole = currentOptionValue.Except(selectedRoleValue).ToList();
foreach (var remove in removeOptionRole)
{
var findOptionRole = _optionRoles.FirstOrDefault(x => x.Id == remove);
if (findOptionRole != null)
{
optionRole.OptionRoles.Remove(findOptionRole);
}
}
return Update(optionRole.OptionRoles);
}
I must have pass a class type of Options when i using this code . it show me this Error :
Severity Code Description Project File Line Suppression State
Error CS1503 Argument 1: cannot convert from 'System.Collections.Generic.ICollection' to 'StoreFinal.Entities.Entities.Identity.OptionRole' StoreFinal.Services C:\Users\Mr-Programer\Desktop\New folder\StoreFinal\StoreFinal.Services\Contracts\Identity\Service\ApplicationOptionRoleManager.cs 97 Active
Error in this line : return Update(optionRole.OptionRoles);
whats the problem ? how can i solve this problem ?
Edit :
Update Method :
public virtual bool Update(T entity)
{
try
{
Entities.Attach(entity);
return true;
}
catch (Exception)
{
return false;
}
}
Look at the Update Method signature:
public virtual bool Update(T entity);
It accepts a param type T which should be One Entity - Why One Entity -- because Entities.Attach() accepts only 1 Object. While what you are passing to it is:
return Update(optionRole.OptionRoles);
Where OptionRoles is of type: ICollection<OptionRole> --
For understandings sake, Change it to
return Update(optionRole.OptionRoles[0]);
or
return Update(optionRole.OptionRoles.First());
And then share the result.

ef core .Include().Contains() null reference exception

I have my models setup like this...
public class Model1 : IEquatable<Model1>
{
public int Model1Id { get; set; }
public string Name1 { get; set; }
public Model2 Model2 { get; set; }
public int Model2Id { get; set; }
public bool Equals(Model1 other)
{
return this.Model2.Equals(other.Model2)
&& this.Name1 == other.Name1;
}
}
public class Model2 : IEquatable<Model2>
{
public int Model2Id { get; set; }
public string Name2 { get; set; }
public bool Equals(Model2 other)
{
return this.Name2 == other.Name2;
}
}
public class ModelContext : DbContext
{
public DbSet<Model1> Model1 { get; set; }
public DbSet<Model2> Model2 { get; set; }
public ModelContext(DbContextOptions<ModelContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Model1>(b =>
{
b.HasOne(m1 => m1.Model2).WithMany().HasForeignKey(m1 => m1.Model2Id);
});
}
}
then I get a null reference exception when I do this...
static void Main(string[] args)
{
var myModel1 = new Model1
{
Name1 = "myName1",
Model2 = new Model2
{
Name2 = "myName2"
}
};
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
try
{
var options = new DbContextOptionsBuilder<ModelContext>()
.UseSqlite(connection)
.Options;
//create database
using(var ctx = new ModelContext(options))
{
ctx.Database.EnsureCreated();
}
//add model objects
using (var ctx = new ModelContext(options))
{
ctx.Database.EnsureCreated();
ctx.Model1.Add(myModel1);
ctx.SaveChanges();
}
//check if exists
using(var ctx = new ModelContext(options))
{
//exception here
bool isExists = ctx.Model1.Include(m1 => m1.Model2).Contains(myModel1);
Console.WriteLine(isExists);
}
}
finally
{
connection.Close();
}
Console.ReadKey();
}
I'm expeting the Model2 instance of my m1 to be populated when I call the Include but it is still null.
but If I add AsEnumerable() to my query like..
ctx.Model1.Include(m1 => m1.Model2).AsEnumerable().Contains(model1);
then everything works fine.
EDIT:
my question is... why do I need to call AsEnumerable()? I was expecting it to work without calling AsEnumerable()..
The difference is one is an entityframe work call the other is linq to objects
Entity Framework Does not understand contains for a CLR Object
public void AddIfNotExists(Model1 model1)
{
//No Need for the include this is executed in sql, assuming the model 2
//property has already been included in your model1 this should work fine
if(false == _context.Model1.Any(x => x.Name1 == model1.Name1
&& x.Model2.Name2 == model1.Model2.Name2))
{
_context.Model1.Add(model1);
}
}
I made this based off of your logic, but chances are you really just want to check if model1.id is the the model1 set. But I have no Idea what your architecture is doing so this is what you probably want

declaring a C# Generic Type method

I have a class:
internal class Paginated<T> where T : class
{
public List<T> data { get; set; }
public int total { get; set; }
public int page { get; set; }
public int per_page { get; set; }
public Paging paging { get; set; }
}
and in another class, I have method:
public T RequestAllPaginated<T>() where T : Paginated<AnyType> //AnyType???
{
var item = Request<T>();
//Get all data from paging property
if(item != null)
{
var i = item as Paginated<AnyType>; //AnyType???
var data = i.data;
}
return item;
}
T is always a Paginated<AnyType> type. The question is how can I determine in RequestAllPaginated method declaration and cast it later on?
Try this solution
public T RequestAllPaginated<T, TK>() where T : Paginated<TK> where TK : class
{
var item = Request<T>();
//Get all data from paging property
if(item != null)
{
var i = item as Paginated<TK>;
var data = i.data;
}
return item;
}
Alternative solution:
public Paginated<T> RequestAllPaginated<T>() where T : class
{
var item = Request<Paginated<T>>();
//Get all data from paging property
if(item != null)
{
var i = item as Paginated<T>;
var data = i.data;
}
return item;
}

EF Code First Configuration of shared Base Class --> Single Configuration File

I am using EF 5.0 Code First in a C# project. I have a base clase which the majority of my domain models derive from.
public abstract class AuditableModelBase
{
public Int32 CreatedByUserId { get; set; }
public DateTime CreatedDate { get; set; }
public virtual UserProfile CreatedByUserProfile { get; set; }
public Int32 UpdatedByUserId { get; set; }
public DateTime UpdatedDate { get; set; }
public virtual UserProfile UpdatedByUserProfile { get; set; }
public AuditableModelBase()
{
CreatedByUserId = 1;
CreatedDate = DateTime.UtcNow;
UpdatedByUserId = 1;
UpdatedDate = DateTime.UtcNow;
}
}
However, for every single entity I have to define the specific configurations to wire these relationships together.
// Relationships
this.HasRequired(amb => amb.CreatedByUserProfile).WithMany().HasForeignKey(amb => amb.CreatedByUserId).WillCascadeOnDelete(false);
this.HasRequired(amb => amb.UpdatedByUserProfile).WithMany().HasForeignKey(amb => amb.UpdatedByUserId).WillCascadeOnDelete(false);
I'm looking for a way to just declare one Configuration similar to the one directly above for the abstract base class instead of having to create an individual configuration file for each of my entities. I'd love to just have one file named "AuditableModelBaseMap.cs" which will have my configuration instead of "Entity1Map.cs", "Entity2Map.cs", "Entity3Map.cs", etc. especially since all of those files have the exact same code inside.
Any advice?
Thanks.
Try it like below but I didnt test it .However if I were you, I wouldnt design Audit tables this way
class AuditableModelBaseMap : EntityTypeConfiguration<AuditableModelBase>
{
public AuditableModelBaseMap ()
{
this.HasRequired(amb => amb.CreatedByUserProfile).WithMany().HasForeignKey(amb => amb.CreatedByUserId).WillCascadeOnDelete(false);
this.HasRequired(amb => amb.UpdatedByUserProfile).WithMany().HasForeignKey(amb => amb.UpdatedByUserId).WillCascadeOnDelete(false);
}
}
THIS IS MY WAY OF Doing AUDITING
public interface IEntity
{
int Id { get; set; }
}
public interface IAuditable : IEntity
{
string UpdatedBy { get; set; }
string CreatedBy { get; set; }
DateTime CreatedDate { get; set; }
DateTime UpdateDate { get; set; }
}
Now any entity which is auditable will implement this class your context will look the following
public class MYContext : DbContext, ILicensingContext
{
private readonly IAuditLogBuilder _auditLogBuilder;
public LicensingContext()
: this(new AuditLogBuilder())
{
}
private IDbSet<Device> Devices { get; set; }
private IDbSet<AuditLog> AuditLogs { get; set; }
public MyContext(IAuditLogBuilder auditLogBuilder)
{
_auditLogBuilder = auditLogBuilder;
}
/// <summary>
/// 1. Constructs the AuditLog objects from the context
/// 2. Calls SaveChanges to save the actual object modified
/// 3. It updates the Log objects constructed in step 1 to populate the IDs returned from the Db
/// 4. Saves the AuditLogs
/// </summary>
/// <returns></returns>
public override int SaveChanges()
{
var entries = ChangeTracker.Entries<IAuditable>().ToList();
_auditLogBuilder.UpdateAuditables(entries);
IEnumerable<AuditLog> auditLogEntities = _auditLogBuilder.ConstructAuditLogs(entries).ToList();
int countOfAffectedRecords = base.SaveChanges();
_auditLogBuilder.UpdateAuditLogs(auditLogEntities);
foreach (AuditLog auditLogEntity in auditLogEntities)
{
GetDbSet<AuditLog>().Add(auditLogEntity);
}
base.SaveChanges();
return countOfAffectedRecords;
}
public IDbSet<TEntity> GetDbSet<TEntity>() where TEntity : class
{
return Set<TEntity>();
}
}
public class AuditLogBuilder : IAuditLogBuilder
{
private string _username;
private string Username
{
get
{
if (HttpContext.Current != null && HttpContext.Current.User != null)
{
_username = HttpContext.Current.User.Identity.Name;
}
if (String.IsNullOrWhiteSpace(_username))
{
_username = "Service Consumer";
}
return _username;
}
}
public IEnumerable<AuditLog> ConstructAuditLogs(IEnumerable<DbEntityEntry<IAuditable>> auditableEntities)
{
var audits = new List<AuditLog>();
if (auditableEntities != null)
{
audits.AddRange(auditableEntities
.Where(
e =>
e.State == EntityState.Modified || e.State == EntityState.Added ||
e.State == EntityState.Deleted)
.SelectMany(GetAuditLogs));
}
return audits;
}
public void UpdateAuditLogs(IEnumerable<AuditLog> auditLogEntities)
{
foreach (AuditLog auditLog in auditLogEntities)
{
auditLog.RecordId = auditLog.Entity.Id;
auditLog.UpdatedBy = auditLog.Entity.UpdatedBy;
if (String.Equals(auditLog.PropertyName, "id", StringComparison.CurrentCultureIgnoreCase))
{
auditLog.NewValue = auditLog.Entity.Id.ToString(CultureInfo.CurrentCulture);
}
}
}
public void UpdateAuditables(IEnumerable<DbEntityEntry<IAuditable>> entries)
{
if (entries != null)
{
foreach (var entry in entries)
{
entry.Entity.UpdateDate = DateTime.UtcNow;
entry.Entity.UpdatedBy = Username;
if (entry.Entity.Id == 0)
{
entry.Entity.CreatedDate = DateTime.UtcNow;
entry.Entity.CreatedBy = Username;
}
}
}
}
private static IEnumerable<AuditLog> GetAuditLogs(DbEntityEntry<IAuditable> entry)
{
var audits = new List<AuditLog>();
string entityName = ObjectContext.GetObjectType(entry.Entity.GetType()).Name;
switch (entry.State)
{
case EntityState.Added:
audits.AddRange(entry.CurrentValues.PropertyNames.Select(propertyName =>
new AuditLog
{
EntityName = entityName,
CreateDate = DateTime.UtcNow,
NewValue =
entry.CurrentValues[
propertyName] != null
? entry.CurrentValues[
propertyName].ToString()
: String.Empty,
PreviousValue = String.Empty,
PropertyName = propertyName,
Entity = entry.Entity,
Action = Actions.Create.ToString()
}));
break;
case EntityState.Deleted:
audits.AddRange(entry.OriginalValues.PropertyNames.Select(propertyName =>
new AuditLog
{
EntityName = entityName,
CreateDate = DateTime.UtcNow,
NewValue = String.Empty,
PreviousValue =
entry.OriginalValues[
propertyName] != null
? entry.OriginalValues[
propertyName].ToString
()
: String.Empty,
PropertyName = propertyName,
Entity = entry.Entity,
Action = Actions.Delete.ToString()
}));
break;
case EntityState.Modified:
audits.AddRange(entry.OriginalValues.PropertyNames.
Where(
propertyName =>
!Equals(entry.OriginalValues[propertyName],
entry.CurrentValues[propertyName]))
.Select(propertyName =>
new AuditLog
{
EntityName = entityName,
CreateDate = DateTime.UtcNow,
NewValue =
entry.CurrentValues[propertyName] != null
? entry.CurrentValues[propertyName].ToString()
: String.Empty,
PreviousValue =
entry.OriginalValues[propertyName] != null
? entry.OriginalValues[propertyName].ToString()
: String.Empty,
PropertyName = propertyName,
Entity = entry.Entity,
Action = Actions.Update.ToString()
}));
break;
}
return audits;
}
}
Have you tried this?
public class AuditableModelBaseMap : EntityTypeConfiguration<AuditableModelBase>
{
public AuditableModelBaseMap()
{
this.HasRequired(amb => amb.CreatedByUserProfile).WithMany().HasForeignKey(amb => amb.CreatedByUserId).WillCascadeOnDelete(false);
this.HasRequired(amb => amb.UpdatedByUserProfile).WithMany().HasForeignKey(amb => amb.UpdatedByUserId).WillCascadeOnDelete(false);
}
}

Categories