I am using this Expression to create my ViewModel from Database Models:
ViewModel.cs:
public class TestViewModel
{
public static Expression<Func<TestModel, TestViewModel>> Projection => dbModel => new TestViewModel
{
Name = dbModel.Name,
PropertiesCommaSeparated = string.Join(", ", dbModel.TestProperties.Select(x => x.TestProperty.Value))
};
public string Name { get; set; }
public string PropertiesCommaSeparated { get; set; }
}
Since string.Join("","") is not supported in Linq to SQL this fails.
According to this answer https://stackoverflow.com/a/934668 I want to use a stored procedure.
Is it possible to execute a stored procedure in an expression like this?
I am using Entity Framework .NET Core 3.1
This is not something you want to do in a ...ViewModel. ViewModels, DTOs ideally shouldn't have any logic unrelated to views (in your case: DB operations).
With that in mind, I'd fetch values beforehand and map those values inside a static factory method [0]:
public class TestViewModel
{
public static TestViewModel FromModel(TestModel model)
{
return new TestViewModel
{
Name = model.Name,
PropertiesCommaSeparated = string.Join(", ", model.TestProperties.Select(it => it.TestProperty.Value))
};
}
public string Name { get; set; }
public string PropertiesCommaSeparated { get; set; }
}
This means you need to make sure you fetch TestProperties property from the database. Assuming TestProperties is a navigation property for a 1-to-many relationship, you would write:
var record = _dbContext.TestModel.Include(e => e.TestProperties).First();
var vm = TestViewModel.FromModel(record)
return View(vm);
One disadvantage here (still going off on the mentioned assumption) is if TestProperty entity is very large with a lot of columns, you might not want to fetch all columns from the database, but only the Value. But from the looks of it, you probably have something like this, so I wouldn't worry too much about it.
class TestProperty
{
public string Key { get; set; }
public string Value { get; set; }
}
[0]: I could have used a constructor here too, but I think static factory FromModel is more expressive of its intent. Both are equally valid approaches.
Related
Hi people of the internet,
I've been searching on SO for a couple of days now, tried loads of possible solutions but I'm not able to solve my issue in the following test-project. Please excuse my lack of skill as I'm a self-taught programmer for just over a year now and I'm this is my first question on SO.
The situation:
I've got 2 entity classes Car and Wheel and want to store and query them from a single db table using c#'s linq to sql(ite).
public class Car
{
public Int64? Id { get; private set; }
public string Manufacturer { get; private set; } = "Porsche";
public double Mileage { get; private set; }
public Wheel Wheel { get; private set; }
public Car()
{
this.Id = Program.CarCount;
Mileage = new System.Random(Program.CarCount).Next(0, 500000);
Program.CarCount++;
}
public Car CreateWheels()
{
// Adds Wheel 2 Car
}
}
public class Wheel
{
public Int64? Id { get; set; }
public string Manufacturer { get; set; } = "Continental";
public Wheel() { Id = Program.WheelCount; }
}
Unfortunately as it is a test project for a different project I'm working on I'm not able to split up the raw-data into multiple OR-tables.
To work with a single table instead I flatten them into a single CarDto class that matches my single db table.
I flatten both classes into the CarDto to be used with LINQ 2 SQLite using AutoMap and assigning the individual db columns.
[Table(Name = "Cars")]
[AutoMap(typeof(Car), ReverseMap = true)]
public class CarDto
{
public CarDto(){ }
[Column(IsPrimaryKey = true)]
public Int64? Id { get; set; }
[Column]
public string Manufacturer { get; set; }
[Column]
public double Mileage { get; set; }
[Column]
public Int64? WheelId { get; set; }
[Column]
public string WheelManufacturer { get; set; }
}
Mapping between DTO-class and entity-class is done with a mapper CarMapper using AutoMapper v.9x
public class CarMapper
{
public IMapper Map => Config.CreateMapper();
IConfigurationProvider Config;
public CarMapper()
{
Config = new MapperConfiguration(cfg =>
{
cfg.AddMaps(typeof(CarMapper).Assembly);
});
Config.AssertConfigurationIsValid();
}
}
Interaction with the db is handled via a repository class CarRepocontaining the mapper and the DataContext class of C# Linq2SQL
public class CarContext : DataContext
{
public Table<CarDto> CarDtos;
public CarContext(IDbConnection connection) : base(connection) { }
}
public class CarRepo
{
private string conn;
CarMapper mp = new CarMapper();
CarContext CarContext => new CarContext(new SQLiteConnection(conn));
public IQueryable<Car> Cars { get => Qry(); }
public CarRepo(string connectionString) { this.conn = connectionString; }
private IQueryable<Car> Qry()
{
return mp.Map.ProjectTo<Car>(CarContext.CarDtos);
}
public List<Car> GetAllCarsFromDb()
{
var dtosFromDb = new List<CarDto>();
using (var db = CarContext)
return mp.Map.Map<List<CarDto>, List<Car>>(db.CarDtos.ToList());
}
public void InsertCars(List<Car> cars)
{
using (var db = CarContext)
{
db.CarDtos.InsertAllOnSubmit(mp.Map.Map<List<Car>, List<CarDto>>(cars));
db.SubmitChanges();
}
}
}
The problem:
Insertion and mapping the CarDto-table to Car works flawlessly. Running repo.Cars.Where(car => car.Id <= 5).ToList(); results as expected except for the nested object Wheel, which always returns as null. The problem exists when projecting a car-query and probably lies in the mapping of the query expression. Looking at the resulting query this seems obvious.
SELECT[t0].[Id], [t0].[Manufacturer], [t0].[Mileage]
FROM[Cars] AS[t0]
WHERE[t0].[Id] <= #p0
Running the following mapping mp.Map.Map<List<CarDto>, List<Car>>(db.CarDtos.ToList()); works and delivers Car including Wheel instances.
I tried loads of solutions (custom mappings,... ) and went through the AutoMapper documentation but wasn't able to fix my problem. Hope someone can help me.
Cheers from germany!
Henrik
EDIT:
P.s. I added a gist https://gitlab.com/snippets/1957648
https://gist.github.com/henrikherr/29eb2913d403ab1d6bede52ed011869a
As #LucianBargaoanu pointed out the ProjectTo doesn't work with ForPath. He also correctly pointed out that the logic/usage of my dto and entities is reversed.
In addition I wouldn't have to do the iqueryable-mapping if my db-design would match my Carand Wheel classes in a more class/object-related way or if my repo would expose and handle the dto class.
You're relying implicitly on ForPath here and that doesn't work with ProjectTo. But you entities/dtos are exactly reversed. Car should be in the DB, with a Wheel FK entity. And CarDto would show up in the UI, or whatever uses the DB. – Lucian Bargaoanu
When using Entity Framework 6, how is the most efficient way to create an object or objects with additional data from other DbSet entities, when I have a DbContext or IQueryable<T>?
Here is some code:
If I have an Data class as follows:
public class Data
{
[Key]
public int id { get; set; }
public string name { get; set; }
public string data { get; set; }
public int parentId { get; set; }
public int otherDataId { get; set; }
}
And an OtherData class as follows:
public class OtherData
{
[Key]
public int id { get; set; }
public string name { get; set; }
public string data { get; set; }
}
In the Data class, the parentId is a foreign key reference to another Data object in the same DbSet, and the otherDataId is a foreign key reference to an OtherData object in a DbSet<OtherData>.
I would like to get all Data objects in the DbSet<Data>, with the additional DbSet data of the parent Data objects id and name and the OtherData object's id and name. I need this to be in one object to be sent from a webservice GET.
I am not sure on how to do this.
Do I need some code along the lines of:
var result = DbContext.Data.Select(x=> x...).Join(y=> y...) .. new { id = x.id... y.name.. }
Can I please have some help with this code?
You can use a join and project the result. In the below snippet CombinedData is a another class with 2 string fields Name and OtherName. You can also use a view but I think the Join is less work.
IQueryable<CombinedData> result = DbContext.Data.Join(
DbContext.Data.DbContext.OtherData,
outer => outer.OtherDataId,
inner => inner.Id),
(outer, inner) => new { Name = outer.Name, OtherName = inner.Name}
);
Depending on your overall architecture, this may be a good or a bad answer but often when faced with this in the past our teams would create a view in the database to combine the fields. Write your optimized query in the view and then treat it like any other table in your data layer.
You could accomplish the same end result using includes or joins or even writing out the expression in a cross-table query but in my opinion the view is the cleanest and most efficient method.
Goal: to save ViewModel object by Entity Framework. I have UserViewModel object which has list of UnitViewModel. Then, I have a UserAdapter class which converts UserViewModel into Entity Framework User object (see Convert()below how).
Now, my question is how do I convert this list of UnitViewModel to its corresponding Entity Framework Unit list? - Do I have to get each object from DB Context by calling something like context.Units.Where(u=>myListofUnitIDs.Contains(u.UnitID))?
public class UserViewModel
{
public Guid? UserID { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public DateTime? CreateTime { get; set; }
public List<UnitViewModel> UserUnits { get; set; }
}
public class UnitViewModel
{
public Guid UnitID { get; set; }
public string Name { get; set; }
public int? SortIndex { get; set; }
public DateTime CreateTime { get; set; }
public bool Assigned { get; set; }
}
public class UserAdapter
{
public static User Convert(UserViewModel userView)
{
User user;
if (userView.UserID.HasValue)
{
using (var provider = new CoinsDB.UsersProvider())
{
user = provider.GetUser(userView.UserID.Value);
}
}
else
{
user = new User();
}
user.FirstName = userView.FirstName;
user.LastName = user.LastName;
user.Password = StringHelper.GetSHA1(userView.Password);
user.UserName = user.UserName;
user.CreateTime = DateTime.Now;
// Problem here :)
// user.Units = userView.UserUnits;
return user;
}
}
UPDATE: The main concern here is that I have to retrieve each Unit from database to match (or map) it with ViewModel.Unit objects, right? Can I avoid it?
For your information, this operation is called as Mapping mainly. So, you want to map your view model object to the entity object.
For this, you can either use already existed 3rd party library as AutoMapper. It will map properties by reflection which have same name. Also you can add your custom logic with After method. But, this approach has some advantages and disadvantages. Being aware of these disadvantages could help you to decide whether you must use this API or not. So, I suggest you to read some articles about advantages and disadvantages of AutoMapper especially for converting entities to other models. One of such disadvantages is that it can be problem to change the name of one property in the view model in the future, and AutoMapper will not handle this anymore and you won't get any warning about this.
foreach(var item in userView.UserUnits)
{
// get the mapped instance of UnitViewModel as Unit
var userUnit = Mapper.Map<UnitViewModel, UserUnit>(item);
user.Units.Add(userUnit);
}
So, I recommend to write your custom mappers.
For example, I have created a custom library for this and it maps objects lik this:
user.Units = userView.UserUnits
.Select(userUnitViewModel => userUnitViewModel.MapTo<UserUnit>())
.ToList();
And I am implementing these mapping functions as:
public class UserUnitMapper:
IMapToNew<UnitViewModel, UserUnit>
{
public UnitViewModel Map(UserUnit source)
{
return new UnitViewModel
{
Name = source.Name,
...
};
}
}
And then in runtime, I am detecting the types of the objects which will be used during mapping, and then call the Map method. In this way, your mappers will be seperated from your action methods. But, if you want it urgently, of course you can use this:
foreach(var item in userView.UserUnits)
{
// get the mapped instance of UnitViewModel as Unit
var userUnit= new UserUnit()
{
Name = item.Name,
...
};
user.Units.Add(userUnit);
}
I have the following entity:
public class SampleClass
{
public int Id { get; set; }
public object Args {get; set; }
}
Because Args can be of different types and doesnt need to be queryable, I want to store it in the Database as a json string.
I know the following workaround would solve my problem:
public class SampleClass
{
public int Id { get; set; }
public object Args { get { return Json.Deserialize(ArgsJson); } set { ArgsJson = Json.Serialize(value); } }
public string ArgsJson {get; set; }
}
But this is pretty ugly as it exposes information not related to the model and it contains logic again not related to the model.
What I would like to do, is something like that:
public class SampleClassMapper : EntityTypeConfiguration<SampleClass>
{
public SampleClassMapper()
{
this.Property(e => e.Args).MapAs<string>(arg => Json.Serialize(arg), str => Json.Deserialize(str));
}
}
Is there any cool way of doing so?
(I'm using .Net 4.0 with EntityFramework 5 and Sql Server 2008 if it helps)
The way that you do is the only one available for now in EF. Currently EF Code First don't have any easy way to change the object serialization but this can be done modifying the EDMX file at runtime.
Please review my code. For every my Entity i've created a service class, where i put all the access methods for this entity.This method are doing the transformation from the Entities to my DTO classes. This methods are called from the Web layer or a bussines method. Am I doing this righth? Or should I do it differently ?
The service method:
public static IEnumerable<OsobaDto> GetNakupyByOsoba(Guid guid)
{
using (FinanceEntities finance = new FinanceEntities())
{
var osoby = from o in finance.OsobaSet
where o.Nakupy.Any(n => n.idnakupu == guid)
select new OsobaDto
{
Id = o.idosoba,
Meno = o.meno,
Priezvisko = o.priezvisko,
Prijem = o.prijem,
Nakupy = o.Nakupy.Select(n => new NakupDto
{
IdNakupu = n.idnakupu,
Cena = n.cena,
Datum = n.datum
})
};
return osoby;
}
}
And the DTO class
public class NakupDto
{
public Guid? IdNakupu
{
get;
set;
}
public Decimal Cena
{
get;
set;
}
public DateTime Datum
{
get;
set;
}
public IEnumerable<OsobaDto> Osoby
{
get;
set;
}
public OsobaDto Platil
{
get;
set;
}
Everything is good, but I have one suggestion. If you have a lot of DTO objects you shoud think about writing a general converter. It can be done using reflection and explicit conversion operators.
Keep in mind the query wont actually hit the database until you actually use the IEnumerable you are are returning (because of deferred execution). I'm not sure how that will work since you are disposing the FinanceEntities before you actually execute the query. Assuming that works, it looks fine to me.