I am trying to implement Insight.Database in a project, and have run into a brick wall trying to utilize the automatic interface implementation AND mapping object properties to odd column names in the database.
I have the following structure...
class Employee
{
string EmployeeCode {get; set;}
string Name {get; set;}
}
class Vacation
{
int VacationId {get; set;}
DateTime VacationDate {get; set;}
string EmployeeCode {get; set;}
string ApprovedByEmployeeCode {get; set;}
Employee Employee {get; set;}
Employee ApprovedByEmployee {get; set;}
}
My database looks like....
Table: Employees (EmployeeCode, [Name])
Table: Vacations (VacationId, VacationDate, EmployeeCode, ApprovedByEmployeeCode)
View: VacationsView (so I don't have to keep writing [and changing]
the same SELECT over and over)
SELECT v.VacationId, v.VacationDate, v.EmployeeCode, v.ApprovedByEmployeeCode, e1.EmployeeCode AS CreatedByEmployeeCode, e1.[Name] AS CreatedByName, e2.EmployeeCode AS ApprovingEmployeeCode, e2.[Name] AS ApprovingName
FROM Vacations v
INNER JOIN Employees e1 ON v.EmployeeCode = e1.EmployeeCode
INNER JOIN Employees e2 ON v.ApprovedByEmployeeCode = e2.EmployeeCode
Stored Procedure: GetAllVacations
SELECT * FROM VacationsView
Finally, with Insight.Database, I am trying to have an interface that will autopopulate my objects and tell it how to use the different column names from my stored procedure for the "employee" properties.
public interface IMyRepository
{
IList<Vacation> GetAllVacations();
}
....
var repo = conn.As<IMyRepository>();
return repo.GetAllVacations();
This works (as in doesn't error) and all the properties of my vacation are correctly mapped, but my two "employee" properties are null (as expected because the column names don't line up to the property names of an employee object). What I can't figure out is how to tell Insight "Use CreatedBy.." fields to build the "Employee" property and "Approving..." fields to build the "ApprovedByEmployee" property.
I have been able to accomplish it using OneToOne with a callback and columnOverride and then use a standard Query(). I.E..
var vacationStructure =
new OneToOne<Vacation, Employee, Employee>(
callback: (vacation, createdBy, approvedBy) =>
{
vacation.Employee = createdBy;
vacation.ApprovedByEmployee = approvedBy;
}, columnOverride: new ColumnOverride[]
{
new ColumnOverride<EmployeeModel>("CreatedByEmployeeCode", "EmployeeCode"),
new ColumnOverride<EmployeeModel>("CreatedByName", "Name"),
new ColumnOverride<EmployeeModel>("ApprovingEmployeeCode", "EmployeeCode"),
new ColumnOverride<EmployeeModel>("ApprovingName", "Name")
});
....
var results = await conn.QueryAsync("GetAllVacations", new {employeeCode}, Query.Returns(_vacationStructure));
However, I'm really trying to utilize the auto interface capabilities of Insight.
Is what I'm trying to do possible?
Assembling repeated child objects isn't something that's currently done automatically, and the interface implementation doesn't give you the right hooks to override the behavior.
Some options:
A. Change the shape of your result set to return the employees as a list with properties.
B. If the classes aren't sealed, derive from Employee so Insight can differentiate between the classes:
public class AssigningEmployee : Employee {
public string AssigningName { get { return Name; } set { Name = Value; } }
...
}
These solutions are all meh. The whole point of Insight.Database is to just work without a lot of extra work. So...
I opened a github issue to track this:
https://github.com/jonwagner/Insight.Database/issues/384
Related
In my ASP.NET MVC application I need to implemenet mapping from one object to another with some kind of UI for mapping configuration in runtime, so the user can define mapping "on the go". Is there any libraries that supports such functionality?
Description
This is objects in my application. I need to somehow allow user to configure mapping of this objects via UI during application runs. For exmaple some kind of page in my application where user will be able to define mapping in simple way like so map Amout of OrderDTO to Order Qty and later without application recompile change this mapping for exmaple for ExactAmmount
//Object in DAL
public class Order
{
public int Id {get; set;}
public string Name {get; set;}
public decimal Qty {get; set;}
//Lots of other fields
}
//Object from XSD generation (for example)
public class OrderDTO
{
public int Id {get; set;}
public string Description {get; set;}
public decimal Ammout {get; set;}
public decimal VAT {get; set;}
public decimal ExactAmmount {get; set;}
//Lots of other fields
}
Note: for legacy reasons I based this answer on AutoMapper 4.2.1 instead of the current 5.x version. The overall approach should be similar with the new version.
It is possible to create different mapping configurations and different mappers within a program. Also, it is possible to create member mappings by member names (string) instead of lambda expressions. However, some static type information is still necessary (as far as my example goes).
See the following example of a profile, that prepares a custom mapping based on property names:
class MemberProfile : Profile
{
private string from;
private string to;
public MemberProfile(string from, string to)
{
this.from = from;
this.to = to;
}
protected override void Configure()
{
this.CreateMap<Order, OrderDTO>()
.ForMember(to, c => c.MapFrom<decimal>(from));
}
}
This could be extended to support different source property types and a collection of custom mappings instead of a single one.
Usage example:
var order = new Order() { Id = 1, Name = "Test", Qty = 0.5m };
var conf1 = new MapperConfiguration(c => c.AddProfile(new MemberProfile("Qty", "Ammout")));
var conf2 = new MapperConfiguration(c => c.AddProfile(new MemberProfile("Qty", "ExactAmmount")));
var res1 = conf1.CreateMapper().Map<OrderDTO>(order);
var res2 = conf2.CreateMapper().Map<OrderDTO>(order);
For res1, Qty is mapped to Ammout and for res2, Qty is mapped to ExactAmmount. Since the difference is described as string property names, it should be possible to let the user influence this configuration.
I have a table that has a FK to another table. I'm using a "view model" class that I want to return instead of the actual EF class. I know I can join the 2 tables and filter on the first table and then make a new view model in the select part based on the fields that I want, but can I do this without the join and instead use the linked table (via FK) in the select to create the view model instance?
ie. this works:
return (from a in context.Articles
join t in context.Topics on a.TopicID equals t.Id
where t.Name == topic
select new ArticleViewModel { Title = a.Title, Introduction = a.Introduction, Content = a.Content }).ToList();
Can I somehow create my ArticleViewModel with the Topics.Article link in a short and clean fashion:
return (from t in context.Topics
where t.Name == topic
select /*how can I create ArticleViewModel from t.Articles?*/ t.Articles).ToList();
Not HQL but Linq, I don't think you can do a select many in HQL, so your left with doing the join, or a sub query...
context.Topics.Where(x => x.Name.Equals(topic))
.SelectMany(y => y.Articals)
.Select(z => new ArticalViewModel { Title = z.Title , etc.. });
It seems you have a DbContext
with a DbSet of Articles. Each Article has a Topic.
with a DbSet of Topics. Each Topic has a collection of Articles that belongs to the topic.
And you want all articles that belong to a topic with the value of variable topic.
If you use entity framework and you have a one to many relation like you have in your Topic class, the "many" part has a foreign key to the "one" and the "one" part has a virtual ICollection of the "many" part
An Article belongs to exactly one Topic. An article "has" a Topic.
A topic has a collection of articles. Each item of the collection belongs to the topic.
This is designed as follows:
public class Article
{
public int Id {get; set;} // by convention will become primary key
public int TopicId {get; set;} // by convertion will become foreign key to Topic
public Topic Topic {get; set;} // an article "has" a Topic
public string Title {get; set;}
public string Introduction {get; set;}
// etc.
}
public class Topic
{
public int Id {get; set;} // by convention primary key
public virtual ICollection<Article> Articles {get; set;}
...
// other Topic properties
}
public class MyDbContext : DbContext
{
public DbSet<Topic> Topics {get; set;}
public DbSet<Article> Articles {get; set;}
}
The following Linq statement will get your sequence of ArticleViewModel items
(I put them in small steps so it's clear what happens
using (var context = new MyDbContext(...))
{
IQueryable<Article> articlesOfTopicsWithTopicName = context.Topics
.Where(topc=> topc.Name == myTopicName)
.SelectMany(topic => topic.Articles);
Whenever you have a sequence of type A and each A has a sequence of type B, and you want all B objects, you use SelectMany.
IQueryable<ArticleViewModel> requestedItems =
articlesOfTopicsWithTopicName.Select(article =>
new ArticleViewModel()
{
Title = article.Title,
Introduction = article.Introduction,
...
}
// delayed execution, nothing has been performed yet
return requestedItems.ToList();
// execute and return the list
}
If you use the database profiler, you will see that that selectMany will do a join on the foreign key in articles with the primary key in topic
I am having class employee which contain complex property Department.
public Class Employee
{
public int Id { get; set;}
public Department {get; set;}
}
public Class Department
{
public int Id {get;set;}
public string Name { get;set;}
}
i create the map for both
(consider the above both class are available in two namespace 'source', 'destination')
Mapper.CreateMap<source.Employee,destination.Employee>()
Mapper.CreateMap<source.Department,destination.Department>()
when i project it.
empQueryable.Project().To<destination.Employee>();
If i saw the NHProfiler
I found that it loads the entity Department
and create the query
select ... from employee left outer join Department .....
i don't know why it loads entity Department, it should make just the projection.
I'm going to go out on a limb here and assume that "destination.Employee" contains references to the "destination.Department". When AutoMapper builds a projection, it does the same as it would as "Mapper.Map". It crawls the destination type, its properties, and its member's properties all the way down. In short, it will build a Select expression something like:
.Select(e => new destination.Employee {
Id = e.Id,
Department = new destination.Department {
Id = e.Department.Id,
Name = e.Department.Name
}
});
You have a few choices here:
Ignore members you don't want mapped in your employee configuration, namely the "Department" property
Create targeted destination types based on use case, and don't share destination types that require different hydrated data based on different needs.
Use explicit expansion (ForMember(d => d.Department, opt => opt.ExplicitExpansion()), then explicitly expand that member in your projection as needed, with the overload for "Project.To" that takes a list of members to expand.
This seems so simple, yet I can't get my head around how I should do it. I would like to keep a list of 'position' names of employees. So, when entering an employee, I should be able to choose either from a list of positions, or simply enter a new one. In the end I should of course only store the ID of the position in the position table.
Tables would be like this
Public Class Position {
public int ID {get; set;}
public string Name {get;set;}
}
Public Class Employee {
public int ID {get; set;}
public string LastName {get;set;}
:
:
public int PositionID {get;set}
}
I think having a type-ahead function which looks up the existing entries as I type, and in case no match is found, I would just post the new position to the position table and somehow get the newly created ID hereof and store this in the employee record. Could this be done - specifically, is there a way to return the key of a newly created record?
Hmm, might have explained myself to a solution - would this be the general way of doing it?
Thanks :)
I agree with user1987322 that this may not be the best solution, but in any case:
If you are using the entity framework you simply associate one entity with a related entity. So with your model you would do this:
using(var context = new EntityContext())
{
Employee emp = new Employee()
{
LastName = lastName,
Position = new Position { Name = name }
};
context.Employees.AddObject(emp);
context.SaveChanges();
}
The new Position entity is brought into the context with the new Employee, and the association fix-up is taken care of by the framework.
We have database which does not have proper foreign keys set. We are now generating edmx using this database. What we want is to set navigation property so that we can get corresponding details from other table. Here is example what exactly we are looking for.
Lets say There is a table Employee and Department. Now in database there is no relation between these tables but Employee has DepartmentId which is taken from Department table.
When we fetch Employee we get only DepartmentID but we also want to get Department as property along with it so that we can get information like "DepartMentName", "Location" which is stored in Department table.
We tried adding Navigation property in EDMX file but it fails and keeps giving error related to relation.
Please help
You can go with something like this. Create a wrapper class for Employee and Department.
public class EmpDept
{
public Employee Employee {get; set;}
public Department Department {get; set;}
}
public IEnumberable<EmpDept> GetEmployeesWithDeptpartment()
{
var result = from e in context.Employee
where e.Id == somevalue
select new EmpDept()
{
Employee = e,
Department = context.Department.Where(d => d.Id == e.DepartmentId)
};
return result.ToList();
}
It means you have an extra class, but it's quick and easy to code, easily extensible, reusable and type-safe.
Hope this helps