Add additional data to model (downcasting?) - c#

I got the following entity model which I use in Entity Framework:
public class User {
public int Id { get; set; }
public string Name { get; set; }
public string EMail { get; set; }
}
Now I'm trying to display to user on a view (MVVM in WPF, MVC in ASP.NET...), but along with other information that isn't available inside the database, but can be fetched at runtime from a service.
For this, I created a derived model class:
public class UserDetail : User {
public bool IsOnline { get; set; }
}
And now some gibberish code that describes what I want to achieve:
var users = _myContext.Users
.ToList()
.Select(x => new UserDetail() {
IsOnline = _myUserService.IsOnline(x.Id)
} = (UserDetail)x); // downcast x (User) to the new UserDetail instance
return View["MyView", users];
Now, downcasting doesn't work that way in C#.. do I have any other options to achieve what I want?

You can add a constructor by copy to UserDetail.
public class UserDetail : User
{
public UserDetail(User x)
{
this.Id = x.Id;
this.Name = x.Name;
this.EMail = x.EMail;
}
public bool IsOnline { get; set; }
}
(that kind of constructor can be generated by T4 if you have many class with this behaviour)
then, change your linq and use that constructor :
var users = _myContext.Users
.ToList()
.Select(x => new UserDetail(x) {
IsOnline = _myUserService.IsOnline(x.Id)
});
return View["MyView", users];

No, you have to copy the properties one by one, or write some code which will do it for you.

Create a separate view model. You shouldn't expand your entity models to accommodate for properties required by your view. Then you can either copy properties one by one as zahorak suggested or use a library specificaly made for this task like AutoMapper.

Related

How to use AutoMapper without Entity Framework?

I am learning how to use AutoMapper. First thing first, I don't use Entity Framework to read my data.
Hence, in my case I have to do manual mapping for each of the properties of my response model.
Below code may help you get more insight of this:
Response model:
public class TotalLossResults
{
public string N_CLAIM_NUMBER { get; set; }
public string N_CLAIM_ID { get; set; }
}
MapperClass:
public class TLResultsMapper : Profile
{
private TotalLossResults tlResultsObj = new TotalLossResults();
public TLResultsMapper()
{
IMappingExpression<DataRow, TotalLossResults> mappingExpression = CreateMap<DataRow, TotalLossResults>();
foreach (var prop in tlResultsObj.GetType().GetProperties())
{
mappingExpression.ForMember(prop.Name, y => y.MapFrom(s => s[prop.Name]));
}
}
}
Note: in the mapper class I used for each to get rid of the mappingExpression.ForMember statement for each property. But this works only when the property name is the same as of the column name (entity name for example) of the result which I get from the database.
I am looking out for some option where I can take similar approach to map the data values to my response model properties when the property's names are not matching with the column names.
I tried doing something like this:
I created another class which has the properties with different names:
public class TLResultsDifferentNames
{
public string N_CLAIM_NUMBER { get; set; }
public string N_CLAIM_ID { get; set; }
}
and a mapper implementation like this:
private TLResultsDifferentNames tlResultsObj = new TLResultsDifferentNames ();
private TotalLossResults tlResultsColObj = new TotalLossResults ();*
for (int i = 0, j = 0; i<tlResultsObj.GetType().GetProperties().Length - 1 && j<tlResultsColObj.GetType().GetProperties().Length - 1; i++, j++)
{
mappingExpression.ForMember(tlResultsObj.GetType().GetProperties()[i].Name, y => y.MapFrom(s => s[tlResultsColObj.GetType().GetProperties()[j].Name]));
}
But this doesn't work. It binds the last column values to all the model properties.
Any help/suggestion to achieve the mapping without using the manual way of mapping would be very helpful.
I could find something really interesting in Auto Mapper today. Which is Attribute Mapping and using that i need not to worry about any sort of manual/dynamical mapping for my models.
Below is the code which works perfectly now for all the properties:
Ex1: here all the properties' names are same
[AutoMap(typeof(object))] //this takes our Source class name
public class TotalLossResults
{
public string N_CLAIM_NUMBER { get; set; }
public string N_CLAIM_ID { get; set; }
}
Ex2: here we got different properties
[AutoMap(typeof(TotalLossResults))] //this takes our Source class name
public class TLResultsDifferentNames
{
[SourceMember(nameof(TotalLossResults.N_CLAIM_NUMBER))]
public string claimNumberOfJack { get; set; }
public string claimIDofJack { get; set; }
}
For mapping configuration we gonna use the below code:
var config1 = new MapperConfiguration(cfg =>
cfg.AddMaps(typeof(TotalLossResults)));
var mapper = new Mapper(config1);
var response = mapper.Map<TotalLossResults>(sourceObject);
Note: Its better to have the configs created in App Start.

Implementing all read api with a intercepting filter using Servicestack.Ormlite

To elaborate what I try to achieve with servicestack.ormlite. Imagine that a franchise business has some branches, each branch has system and local database, all of these database are replicating each other. In the system, each model is with a property called store_id like below.
public class UserEntity : EntityBase
{
[PrimaryKey, AutoIncrement]
public int id { get; set; }
public string user_id { get; set; }
public string name { get; set; }
public string email { get; set; }
public string password { get; set; }
public int role { get; set; }
}
public class EntityBase
{
public int store_id {get;set;}
public bool is_delete {get;set;}
}
We have 40+ entity and repos, is there any way to have all servicestack.ormlite read api filtered by store_id in one action instead of coding repo by repo ? I've a abstract repobase from which all repos are derived. And some repos needs to read all data across different store_id.
any help is much appreciated !!
This question is still unclear on what answer it wants, the screenshot says it doesn't know which API to use to filter by store_id but your screenshot includes 2 different examples of filtering by store_id?
db.Where<T>(new { store_id = _store_id });
db.Where<T>("store_id", _store_id);
Both of which should work. Although I'd recommend using the Typed version when possible, you can also use nameof() instead of magic strings:
db.Where<T>(nameof(EntityBase.store_id), _store_id);
Maybe you're after different examples of doing the same thing inside a generic repo?
You can also query using a typed SqlExpression<T>:
var q = db.From<T>().Where(x => (x as EntityBase).store_id == _store_id);
var all = db.Select(q);
Or if you want to combine it with an additional typed expression:
var q = db.From<T>().Where(x => (x as EntityBase).store_id == _store_id);
var filtered = db.Select(q.And(expr));
Since you're already using generic constraints, you can also add a constraint that the entity must be a EntityBase as well, e.g:
class RepoBase<T> where T : EntityBase, new() { ... }
That way you can query without casting, e.g:
var q = db.From<T>().Where(x => x.store_id == _store_id);
var all = db.Select(q);
and
var q = db.From<T>().Where(x => x.store_id == _store_id);
var filtered = db.Select(q.And(expr));

how to add new type to an existing class or object in asp.net mvc

i want to add a new type to an existing class(object?) that fetch from database table (with entity framework class) in Microsoft MVC.
i.e:
my person class {f_name, l_name}
how can i add new type to this objects when i loop throw it?
like:
// Person class stracture is:
public Person()
{
public string f_name { get; set; }
public string l_name { get; set; }
}
i want to add 'age' to it without add this to model
var all_persons = db.Person.toList();
for (var item in all_person)
{
item.age = some_value;
}
return View(all_persons);
Add a [NotMapped] (assuming you dont want this property to be mapped to database) in your Person class:
[NotMapped]
public int Age {get; set;}
Or create a view model instead of using EF model directly
Example using Entity Framework :
If you are using code-first approach then [NotMapped] will work fine for you.
But if you are using model-first approach then it will not work because if you are going to update your model its going to be updated according to .tt template of model and create a class with only properties in tables.
So, what now ? here comes the concept of partial class.
model generated from EF :
namespace EF.Model
{
public partial class Person
{
public string f_name { get; set; }
public string l_name { get; set; }
}
}
So to add additional properties that you don't want EF to map while CRUD operations. Add a new partial class in the same project with same class name and same namespace as of EF model
namespace EF.Model
{
public partial class Person
{
public string fullName { get; set; }
public int age { get; set; }
}
}
So now you can do like this.
var all_persons = db.Person.toList();
for (var item in all_person)
{
item.age = some_value;
item.fullName = item.f_name + item.l_name;
}
return View(all_persons);
I hope this would give you a better understanding. You should read about partial classes.
https://www.dotnetperls.com/partial
You can add the new property in a linq Select as follows:
void AddNewField(int number)
{
var all_persons = new db.Person.Select(x => new { x.f_name, x.l_name, age = number });
return View(all_persons);
}
This will save you having to actually add the new field to the model.

Best way to add base class values to derived class?

I have a reference file (dll) containing a class, which i use as my base class:
public class Group
{
public Group();
public int Id { get; set; }
public int League_Id { get; set; }
public string Name { get; set; }
public string Nationality { get; set; }
}
(Please note that the Group class contains about 30 entities, i have only displayed some)
Since i use Entity Framework the ID needs to be unique. i get this data from a API call and noticed that the ID is not unique, i have added my own class.
public class modGroup : APICall.Group
{
public modGroup()
{
modID = 0;
}
[Key]
public int modID { get; set; }
}
This setup works, EF is creating the database.
What i'd like to do is get the data from the API (Which is structured as the Group class), create a new modGroup() and set all data from the API call, without referencing each individual object.
What i would like to do is transfer data without setting each individual entity.
List<APICall.Group> groupData= _ApiRequester.GetGroup();
using (var a = new databaseModel())
{
foreach (APICall.Group x in groupData)
{
var modGroup = new Models.modGroup();
modGroup.modID = 0;
// fill modgroup with rest of variables without doing:
// modGroup.League_iD = a.League_iD;
// modGroup.Name = a.Name;
// etc
}
}
I would use Automapper to map the two classes together in one call. This type of situation is what it was designed for. All you would need to do is create a mapping configuration and then map the two classes. It's a very simple but powerful tool. e.g. Something like this:
var config = new MapperConfiguration(cfg => cfg.CreateMap<APICall.Group, modGroup>()
.ForMember(dest => dest.modID, s=>s.MapFrom(s=>s.Id));
// etc create more mappings here
);
var mapper = config.CreateMapper();
List<modGroup> modGroupList = mapper.Map<List<modGroup>>(groupData);

Convert an Entity to a extended class based on original class?

I have a Employee class which is an Entity Framework code first class representing an Employee. I would like to create a view model based on the original Employee class and then populate that class from a linq query to my EF context.
public class EmployeeVM : Employee
{
public List<DepartmentSelect> Departments { get; set; }
}
EmployeeVM employee = context.Employees.Find(id);
I get the error "cannot implicitly convert type Employee to EmployeeVM."
Is there a simple way to do this rather then creating a new object and foreaching every parameter into the equivalent in the new class?
be nice if coding had an easy button, but it is what it is.. You could write the code one time and reuse it if you want. You could use AutoMapper and deal with some of the headaches that come with that. Your best bet would just be to write it yourself and maybe catch some errors if your context changes..
Without a mapper you could just add a static func to your viewmodel that will take an Employee object and create an EmployeeVM and use this in your context queries.
public class EmployeeVM
{
public EmployeeVM()
{
Departments = new List<DepartmentSelect>();
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime? Dob { get; set; }
public List<DepartmentSelect> Departments { get; set; }
public static Func<Employee, EmployeeVM> FromEntity = item => new EmployeeVM() {
Id = item.Id,
FirstName = item.FirstName,
LastName = item.LastName,
Dob = item.Dob
};
}
// get single EmployeeVM
var eVm = EmployeeVM.FromEntity(context.Employees.Find(id));
// get List<EmployeeVM
var eVmList = context.Employees.Select(EmployeeVM.FromEntity).ToList();
This isnt recommended but if Employee is a partial class you could always just extend it by adding another partial class in the same namespace.
public partial class Employee
{
//Add Extra Properties
public List<DepartmentSelect> Departments { get; set; }
}
you maybe want to use Automapper. http://automapper.org/
Tools like AutoMapper are designed to ease the burden of having a bunch of property-mapping code. You can also just serialize the first object and deserialize it into the second one.
I should probably mention, though, that this is probably a misuse of inheritance. Is there a reason you can't just put your Employee entity directly on your EmployeeVm as a property? Beyond that, Arash is right in pointing out that ViewModels should generally not be tightly coupled to your data model.

Categories

Resources