Where to map DTOs to Entites for Create/Add method in repository - asp.net-web-api2

I have a Web Api Application where I have EF DB first entities and DTO classes.
So this is my generic repository interface -
public interface IRepository<TEntity> where TEntity:class
{
IQueryable<TEntity> GetAll();
void Create(TEntity Entity);
TEntity GetById(int id);
void Update(TEntity Entity);
void Remove(int id);
}
Here is a sample of the GetAll method implementation for one of the services -
public class OrdersRepository : IRepository<SalesOrderHeader>
{
private AdventureWorksEntities db = new AdventureWorksEntities();
public IQueryable<SalesOrderHeader> GetAll()
{
return db.SalesOrderHeaders;
}
And here is my service or apicontroller calling the method along with additional mapping -
public IQueryable<Orders> GetSalesOrderHeaders()
{
**var Orders = orderRepo.GetAll();**
var OrderDetails = orderDetailRepo.GetAll();
return (from so in Orders
select new Orders()
{
SalesOrderID = so.SalesOrderID,
SalesOrderNumber = so.SalesOrderNumber,
ShipDate = so.ShipDate.ToString(),
Customer = customerRepo.GetById(so.CustomerID),
OrderItems = (from sod in OrderDetails
select new OrderItems()
{
SalesOrderId = sod.SalesOrderID,
ProductID = sod.ProductID,
Quantity = sod.OrderQty,
UnitPrice = sod.UnitPrice
}).Where(a => a.SalesOrderId == so.SalesOrderID).ToList()
});
}
As seen here the mapping is being done here in the apicontroller class. Similarly for Create/Add method of repository, where will this happen? If in the apicontroller, does that mean I need access to Entity in my apicontroller? if in the Repository, then i will have to map the DTO to Entity in my repository. Both seem dubious. Here is my DTO class -
public class Orders
{
public int SalesOrderID { get; set; }
public string SalesOrderNumber { get; set; }
public string ShipDate { get; set; }
public CustomerDTO Customer { get; set; }
public List<OrderItems> OrderItems { get; set; }
}
The entity class is called SalesOrderHeaders and has a lot more fields.

Neither. Like you, I faced this dilemma with my code and decided that implementing object mapping in aither the controller or the repository will only add innecesary complexity and will end up violating the DRY (Don't Repeat Yourself) principle, and since using an ORM was overkill for the size of the project, I end up creating a set of extension methods that allowed me do the job in a clean and reusable way.
Here is an example, though incompleted, that may help you build your own:
// File: ModelExtensions.Orders.cs
public static partial class ModelExtensions
{
public static IEnumerable<OrdersDto> MapToDto(this IEnumerable<Orders> list)
{
return list.select(item => item.MapToDto());
}
public static OrdersDto MapToDto(this Orders order)
{
var dto = new OrdersDto() {
SalesOrderID = order.SalesOrderID,
SalesOrderNumber = order.SalesOrderNumber,
ShipDate = order.ShipDate.ToString()
}
}
public static IEnumerable<OrdersDetailsDto> MapToDto(this IEnumerable<OrderDetails> list)
{
return list.select(item => item.MapToDto());
}
public static OrderDetailsDto MapToDto(this OrderDetails orderDetails)
{
var dto = new OrderDetailsDto() {
SalesOrderId = orderDetails.SalesOrderID,
ProductID = orderDetails.ProductID,
Quantity = orderDetails.OrderQty,
UnitPrice = orderDetails.UnitPrice
}
}
}
You can also create similar mapping functions to map from DTOs to Entities.
To use this functions, just include a reference to the namespace where the extension methods are defined, et voila, you have all your mappings at hand.
Basically, the transformation would occur in the API method, since there is where the exchange (DTO/Entity or Entity/DTO) is made.
Using this method, you could use the mapping functions wherever you need it without the need to repeat (DRY principle) the logic in different places.
Sample usage:
public IQueryable<Orders> GetSalesOrderHeaders()
{
var orders = orderRepo.GetAll().MapToDto();
var orderDetails = orderDetailRepo.GetAll();
foreach (var ord in orders)
{
ord.OrderItems = orderDetails.Where(a => a.SalesOrderId == ord.SalesOrderID).MapToDto();
}
return orders.ToList();
}

Related

Entity framework Core Raw SQLQueries with custom model

Using Entity Framework 6, I was able to use execute a Raw SQL Query and use a custom model which was not defined in the DBContext in order to store the output of the query. A simple example is the following:
List<MyModel> data = context.Database.SqlQuery<MyModel>("SELECT Orders.OrderID, Customers.CustomerName FROM Orders INNER JOIN Customers ON Orders.CustomerID=Customers.CustomerID;").ToList();
I execute one SQL command and I expect a list of custom models.
I try to do something similar with Entity Framework Core and the closest example that I found will force me to define a property from DBContext. This will not allow me to use a custom model to fill the data that SQL server will return.
var books = context.Books.FromSql("SELECT * FROM Books").ToList();
This query informs Entity Framework Core that the query will return a list of books. Is there a way to implement something like this in Entity Framework Core?
From .NET Core 2.1:
Add modelBuilder.Query<YourModel>() to OnModelCreating(ModelBuilder modelBuilder)
Use context.Query<YourModel>().FromSql(rawSql) to get data
Here's how I was able to get this working (for completeness):
MyModel.cs:
public class MyModel
{
// The columns your SQL will return
public double? A { get; set; }
public double? B { get; set; }
}
Add class that just inherits from your original EF context class (i called mine DbContextBase):
public class DbContext : DbContextBase
{
public virtual DbSet<MyModel> MyModels { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Necessary, since our model isnt a EF model
modelBuilder.Entity<MyModel>(entity =>
{
entity.HasNoKey();
});
}
}
Use that class (instead of your original EF context class):
// Use your new db subclass
using (var db = new DbContext())
{
var models = await db.MyModels.FromSqlRaw(...).ToListAsync(); // E.g.: "SELECT * FROM apple A JOIN banana B ON A.col = B.col"
}
Notes:
If you need to, just use FromSqlInterpolated instead of
FromSqlRaw
The "db context" subclass allows you to update EF models without affecting your "polyfill" code
Works with SQL Server stored procs that return only 1 result set
The question was about .NET Core 2. Now I have a solution and I am going to write it here so that someone else could use it in case he/she needs it.
First of all we add the following method in dbContext class
public List<T> ExecSQL<T>(string query)
{
using (var command = Database.GetDbConnection().CreateCommand())
{
command.CommandText = query;
command.CommandType = CommandType.Text;
Database.OpenConnection();
List<T> list = new List<T>();
using (var result = command.ExecuteReader())
{
T obj = default(T);
while (result.Read())
{
obj = Activator.CreateInstance<T>();
foreach (PropertyInfo prop in obj.GetType().GetProperties())
{
if (!object.Equals(result[prop.Name], DBNull.Value))
{
prop.SetValue(obj, result[prop.Name], null);
}
}
list.Add(obj);
}
}
Database.CloseConnection();
return list;
}
}
Now we can have the following code.
List<Customer> Customers = _context.ExecSQL<Customer>("SELECT ......");
follow these steps:
Create your model
Probably it could be better if you can reduce it to a model as generic as possible but it's not a must:
public class MyCustomModel
{
public string Text { get; set; }
public int Count { get; set; }
}
Add it to your own DbContext
Create DbSet for your custom model
public virtual DbSet<MyCustomModel> MyCustomModelName { get; set; }
Keep in mind to specify your custom model has no key
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
...
modelBuilder.Entity<MyCustomModel>().HasNoKey();
}
Use it from your dbContext instance
async public Task<List<MyCustomModel>> GetMyCustomData()
{
var rv = new List<MyCustomModel>();
using (var dataContext = new DbContext())
{
var sql = #"
select textField as 'Text', count(1) as 'Count'
from MyTable";
rv = await dataContext.Set<MyCustomModel>().FromSqlRaw(sql).ToListAsync();
}
return rv;
}

Initializing referenced objects in entity framework unit of work

I have a class in Entity framework 5 (using MVC 4):
public class JobFunction
{
public int Id { get; set; }
public string JobFunctionName { get; set; }
public int StatusId { get; set; }
public Status JFStatus { get; set; }
}
In my OnModelCreating method, I establish a FK relationship with the Status table as follows:
modelBuilder.Entity<JobFunction>().HasRequired(a => a.JFStatus).
WithMany().HasForeignKey(u => u.StatusId).WillCascadeOnDelete(false);
In my controller, I get a list of JobFunction objects as follows:
List<JobFunction> jfList = uow.JobFunctionRepository.GetAll().ToList<Catalog>();
where uow is my Unit of Work object, and JobFunctionRepository is defined. When I examine any JobFunction object in jfList, I see the following in my watch window:
Id: 1
JfStatus: null
JobFunctionName: "Manager"
StatusId: 2
Note that JFStatus is null. My question is: what provisions do I make in my code to initialize JFStatus to the appropriate Status object (based on the value of StatusId), during my GetAll call?
Thanks in advance.
-Zawar
You need some instrument to apply eager loading when you load the data through your repository. For example you could give your GetAll method a parameter list of expressions for the navigation properties you want to include in your query:
using System.Data.Entity;
//...
public IQueryable<JobFunction> GetAll(
params Expression<Func<JobFunction, object>>[] includes)
{
IQueryable<JobFunction> query = context.JobFunctions;
foreach (var include in includes)
query = query.Include(include);
return query;
}
Then you call it like so:
List<JobFunction> jfList = uow.JobFunctionRepository
.GetAll(jf => jf.JFStatus)
.ToList();
The JFStatus property should be filled now.

Best Practice with MVC4 and EF5 to apply changes

I have a CustomerOrder-view where I would like to change an existing CustomerOrder.
I have a viewmodel that very simpliefied looks something like this:
public class CustomerOrderViewModel
{
public int ID { get; set; }
public Customer Customer { get; set; }
public DateTime Date { get; set; }
public IEnumerable<OrderRow> OrderRows { get; set; }
}
public class OrderRow
{
public int id { get; set; }
public int price { get; set; }
}
public class Customer
{
public string Name { get; set; }
}
I also have a database with mapping tables / fields.
In my GET Action Method I load the Order with the help of Automapper like this:
var customerOrder = using (var ctx = new My_Entities()) {
return ctx.CustomerOrders.
Include("Orderrows").
Include("Customer").
Single(o => o.CustomerOrderID == id);
}
var model= AutoMapper.Mapper.DynamicMap<DataAccessLayer.CustomerOrder, CustomerOrderViewModel>(customerOrder);
In the View I use Knockout to bind to a viewmodel there, where the user can update the CustomerOrder. That includes editing Customer information and adding new orderrows etc.
Then in the post back a map the ViewModel back to the ObjectContext CustomerOrder:
var customerOrderToBeSaved =
AutoMapper.Mapper.DynamicMap<CustomerOrderViewModel, CustomerOrder>(
customerOrderViewModel);
try
{
using (var ctx = new MyEntities())
{
ctx.CustomerOrders.Attach(customerOrderToBeSaved);
ctx.CustomerOrders.ApplyCurrentValues(customerOrderToBeSaved);
...
ctx.SaveChanges();
}
}
I get the error message: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
OK, that I can understand. But how should I go about this? Can I get the existing object and apply Changes to that one, because that is really what I'd like. I've tried to look up the old one and detach it but I haven't got it to wrok.Perhaps I'm doing this in a completely wrong way. Please advice.
You should not attach customerOrderToBeSaved, see MSDN about the argument of ApplyCurrentValues.
The detached object that has property updates to apply to the original object.
So you've got to load the entity from the database into the context and then ApplyCurrentValues with the detached object that has the new values.
You don't have to load the row from the database to update it.
You can do something like this:
var entity = ctx.CustomerOrders.Attach(customerOrderToBeSaved);
ctx.Entry( entity ).State = EntityState.Modified;
ctx.SaveChanges();
This will tell EF to issue an UPDATE SQL statement that overwrites all the columns in the record.
You can select which columns you want to update like this:
var entity = ctx.CustomerOrders.Attach(customerOrderToBeSaved);
var entry = ctx.Entry( entity );
entry.Property( o => o.<ColumnPropertyToUpdate> ).IsModified = true;
entry.Property( o => o.<ColumnPropertyToUpdate> ).IsModified = true;
...
ctx.SaveChanges();
If you do this, EF will only include the columns you've marked as modified in the UPDATE statement.

Context variables in Ninject 2

I found this article on Context Variables in an earlier version of Ninject. My question is two-fold. First, how can I get this behavior with Ninject 2? Secondly, do context variables carry through down the request chain? For example, let's say I wanted to replace these calls:
var a = new A(new B(new C())));
var specialA = new A(new B(new SpecialC()));
... with this:
var a = kernel.Get<A>();
var specialA = kernel.Get<A>(With.Parameters.ContextVariable("special", "true"));
Is it possible to set up a binding like this, where the context remembers that it is in a "special" context when it comes time to construct a C?
Here's some stuff that I use against V2, with ~0 effort to clean it up for you - let me know if you can't disentagle it.
As you surmised, there doesn't seem to be a really explicit API that surfaces the "context parameter, even for nested resolutions" stuff in v2 as-is (it's presence is buried as the 3rd parameter on an overload of the Parameter ctor).
public static class ContextParameter
{
public static Parameter Create<T>( T value )
{
return new Parameter( value.GetType().FullName, value, true );
}
}
public static class ContextParameterFacts
{
public class ProductId
{
public ProductId( string productId2 )
{
Value = productId2;
}
public string Value { get; set; }
}
public class Repository
{
public Repository( ProductId productId )
{
ProductId = productId;
}
public ProductId ProductId { get; set; }
}
public class Outer
{
public Outer( Repository repository )
{
Repository = repository;
}
public Repository Repository { get; set; }
}
public class Module : NinjectModule
{
public override void Load()
{
Bind<ProductId>().ToContextParameter();
}
}
//[ Fact ]
public static void TwoDeepShouldResolve()
{
var k = new StandardKernel( new Module() );
var o = k.Get<Outer>( ContextParameter.Create( new ProductId( "a" ) ) );
Debug.Assert( "a" == o.Repository.ProductId.Value );
}
}
And here's some code [that'll confuse the matter] which demonstrates how I apply it in my context:-
public class ServicesNinjectModule : NinjectModule
{
public override void Load()
{
Bind<ProductId>().ToContextParameter();
Bind<Func<ProductId, ResourceAllocator>>().ToConstant( ( productId ) => Kernel.Get<ResourceAllocator>(
ContextParameter.Create( productId ) ) );
}
}
public static class NinjectContextParameterExtensions
{
public static IBindingWhenInNamedWithOrOnSyntax<T> ToContextParameter<T>( this IBindingToSyntax<T> bindingToSyntax )
{
return bindingToSyntax.ToMethod( context => (T)context.Parameters.Single( parameter => parameter.Name == typeof( T ).FullName ).GetValue( context ) );
}
}
As usual, you should go look a the source and the tests - they'll provide you with a far more detailed and relevant answer than I can.

Fluent NHibermate and Polymorphism and a Newbie!

I'm a fluent nhibernate newbie and I'm struggling mapping a hierarchy of polymorhophic objects. I've produced the following Model that recreates the essence of what I'm doing in my real application.
I have a ProductList and several specialised type of products;
public class MyProductList
{
public virtual int Id { get; set; }
public virtual string Name {get;set;}
public virtual IList<Product> Products { get; set; }
public MyProductList()
{
Products = new List<Product>();
}
}
public class Product
{
public virtual int Id { get; set; }
public virtual string ProductDescription {get;set;}
}
public class SizedProduct : Product
{
public virtual decimal Size {get;set;}
}
public class BundleProduct : Product
{
public virtual Product BundleItem1 {get;set;}
public virtual Product BundleItem2 {get;set;}
}
Note that I have a specialised type of Product called BundleProduct that has two products attached.
I can add any of the specialised types of product to MyProductList and a bundle Product can be made up of any of the specialised types of product too.
Here is the fluent nhibernate mapping that I'm using;
public class MyListMap : ClassMap<MyList>
{
public MyListMap()
{
Id(ml => ml.Id);
Map(ml => ml.Name);
HasManyToMany(ml => ml.Products).Cascade.All();
}
}
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(prod => prod.Id);
Map(prod => prod.ProductDescription);
}
}
public class SizedProductMap : SubclassMap<SizedProduct>
{
public SizedProductMap()
{
Map(sp => sp.Size);
}
}
public class BundleProductMap : SubclassMap<BundleProduct>
{
public BundleProductMap()
{
References(bp => bp.BundleItem1).Cascade.All();
References(bp => bp.BundleItem2).Cascade.All();
}
}
I haven't configured have any reverse mappings, so a product doesn't know which Lists it belongs to or which bundles it is part of.
Next I add some products to my list;
MyList ml = new MyList() { Name = "Example" };
ml.Products.Add(new Product() { ProductDescription = "PSU" });
ml.Products.Add(new SizedProduct() { ProductDescription = "Extension Cable", Size = 2.0M });
ml.Products.Add(new BundleProduct()
{
ProductDescription = "Fan & Cable",
BundleItem1 = new Product() { ProductDescription = "Fan Power Cable" },
BundleItem2 = new SizedProduct() { ProductDescription = "80mm Fan", Size = 80M }
});
When I persist my list to the database and reload it, the list itself contains the items I expect ie MyList[0] has a type of Product, MyList[1] has a type of SizedProduct, and MyList[2] has a type of BundleProduct - great!
If I navigate to the BundleProduct, I'm not able to see the types of Product attached to the BundleItem1 or BundleItem2 instead they are always proxies to the Product - in this example BundleItem2 should be a SizedProduct.
Is there anything I can do to resove this either in my model or the mapping?
Thanks in advance for your help.
As it stands, the BundleItem1 and BundleItem2 properties will always have a Product proxy because NH creates your proxies without touching the database, so it doesn't know if they are Products or some derived type. But when you call a method on your bundle items, NH should hit the DB and load the correct record, and you should get polymorphic behavior.
You could test this out. Add an override of ToString to your SizedProduct:
public override string ToString()
{
return "I'm a sized product!";
}
Then load your BundleProduct and do this:
Debug.WriteLine(bp.BundleItem1.ToString());
Debug.WriteLine(bp.BundleItem2.ToString());
You should find that the second call prints out "I'm a sized product!", and this will demonstrate that you have working polymorphism.
Assuming this all worked as I've described, its time to tackle the real question: what exactly do you want to do? Maybe you could provide some code that doesn't actually work as you would like it to.