Classes:
public class Department
{
public string Name { get; set; }
}
public class DepartmentA : Department
{
public string NameA { get; set; }
}
public class Employee
{
public string Name { get; set; }
public int Salary { get; set; }
public string Address { get; set; }
public Department Department { get; set; }
}
public class EmployeeDTO
{
public string Name { get; set; }
public int Salary { get; set; }
public string Address { get; set; }
public Department Department { get; set; }
}
Code:
DepartmentA departmentA = new DepartmentA { NameA = "depA", Name = "dep" };
Employee emp = new Employee
{
Name = "James",
Salary = 20000,
Address = "London",
Department = departmentA
};
//Mapster
var empDTO = emp.Adapt<EmployeeDTO>();
//AutoMapper
//var config = new MapperConfiguration(cfg => cfg.CreateMap<Employee, EmployeeDTO>());
//var mapper = config.CreateMapper();
//EmployeeDTO empDTO = mapper.Map<EmployeeDTO>(emp);
Console.WriteLine("SourceType:" + emp.Department.GetType().Name);
Console.WriteLine("DestType:" + empDTO.Department.GetType().Name);
If you run the code you will see that SourceType and DestType are not the same. How do I achieve that they are the same? Mapster mapps department as Department class (ancestor) and not as DepartmentA class (descendant).
In this example I know that there is descendant in emp.Department but in my app I won't know. So the solution of this problem must be generic.
I tried to solve this problem in AutoMapper. Got the same results. As #Prolog wrote below, in AutoMapper is working. I updated main code. I am still interested if someone solved issue in Mapster.
You need to use ShallowCopyForSameType, AutoMapper will perform deep copy only declared types, Mapster perform deep copy for all types.
Related
I have those 2 Models
public class BranchEmployees
{
public int ID { get; set; }
[Required, Column(Order = 0), Key]
public string ApplicationUserID { get; set; }
[Required, Column(Order = 1), Key]
public int BranchID { get; set; }
public virtual ICollection<ApplicationUser> ApplicationUser { get; set; }
public virtual ICollection<Branch> Branch { get; set; }
}
public class Branch
{
public int ID { get; set; }
public string BranchName { get; set; }
[Required]
public string ApplicationUserID { get; set; }
public ApplicationUser User { get; set; }
public virtual ICollection<BranchEmployees> BranchEmployees { get; set; }
}
public class ApplicationUser
{
//rest of the code
}
UPDATE
I have everything set up but what I want is the query that gets me the Employees whose IDs are in the branch employees table
, I'm using entity framework code first with MVC 5 , how do I do it ?
Assuming that your ApplicationUser class will have a navigational property called BranchEmployees, here is the query that gets me the Employees whose IDs are in the branch employees table
List<ApplicationUsers> employeeNames =
dbContext
.ApplicationUsers
.Where(au => au.BranchEmployees
.Count() > 0).ToList();
Also, can you provide whole model including ApplicationUser? I also wonder why you do not prefer BranchEmployees to inherit from ApplicationUser.
You don't need a class that indicates a many-to-many relation between two tables when you do code-first. The key here is to create virtual properties of those classes. Lets say you have a class Student and class Course. Students can be in many Courses and Courses can have many Students. To generate a database using these models the classes should look like this:
public class Student
{
private ICollection<Course> _courses;
public Student()
{
this._courses = new HashSet<Course>();
}
[Key]
public int Id { get; set; }
public string FullName { get; set; }
public virtual ICollection<Course> Courses
{
get { return this._courses; }
set { this._courses = value; }
}
}
And for Course:
public class Course
{
private ICollection<Student> _students;
public Course()
{
this._students = new HashSet<Student>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Student> Students
{
get { return this._students; }
set { this._students = value; }
}
}
I hope that this can help you solve your issue.
how do I insert data in many to many relationship?
I have these two models definded by this code
public class Article
{
public int ArticleID { get; set; }
public string Title { get; set; }
public DateTime Date { get; set; }
public string Anotation { get; set; }
public string Body { get; set; }
public virtual ICollection<ArticleTag> ArticleTags { get; set; }
}
public class ArticleTag
{
public int ArticleTagID { get; set; }
public string TagName { get; set; }
public virtual ICollection<Article> Articles { get; set; }
}
Now the big Q is how to seed the database with test data. I have some experience with normal tables and queries and in MVC EF I am quite a novice. Normally I would create an association table and in it define the link between these models. But I have read many tutorials and none gave me a deterministic way how to do it, so I you can imagine my confusion.
As I said before, I "created" an assoc table via ModelBuilder and that's where I end :(
public class DatabaseContext : DbContext
{
public DbSet<Article> Articles { get; set; }
public DbSet<ArticleTag> ArticleTags { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Article>().
HasMany(at => at.ArticleTags).WithMany(a => a.Articles).
Map(m => m.MapLeftKey("ArticleID").MapRightKey("ArticleTagID").ToTable("Articles_To_ArticleTags"));
}
}
I need to know the easiest way to seed my DB (don't care if have to delete the assoc table) and understand how it works in the MVC - linking the two models, linking two specific instances, ... , and how to code it all.
Thx for any answer.
So finally solved!
Found a good post here and with this one together I made it work.
In short what I had to do:
Remove my builder link - EF itself creates link (own assoc table)
A little modify models
Create DatabaseInitializer
In the Global.asax to the method Application_Start() add Database.SetInitializer(new DatabaseInitializer()); declaration
don't forget to se ConnectionString and DBcontext
Normally use in a View
Modifications in constructor (not sure if absolutely necessary, maybe it does the same thing as native/default constructor):
public class Article
{
public int ArticleID { get; set; }
public string Title { get; set; }
public DateTime Date { get; set; }
public string Anotation { get; set; }
public string Body { get; set; }
public string SourceLink { get; set; }
public virtual List<ArticleTag> ArticleTags { get; set; }
public Article()
{
ArticleTags = new List<ArticleTag>();
}
}
public class ArticleTag
{
public int ArticleTagID { get; set; }
public string TagName { get; set; }
public virtual List<Article> Articles { get; set; }
public ArticleTag()
{
Articles = new List<Article>();
}
}
And the DatabaseInitializer
public class DatabaseInitializer : DropCreateDatabaseIfModelChanges<DatabaseContext>
{
protected override void Seed(DatabaseContext context)
{
ArticleTag tag1= new ArticleTag { TagName = "aaaa" };
ArticleTag tag2= new ArticleTag { TagName = "bbbb" };
ArticleTag tag3= new ArticleTag { TagName = "cccc" };
var articleTags = new List<ArticleTag> { tag1, tag2, tag3};
articleTags.ForEach(i => context.ArticleTags.Add(i));
context.SaveChanges();
Article a1 = new Article
{
Title = "Title1",
Date = DateTime.Now,
Anotation = "Anotation1",
Body = "article_1",
ArticleTags = new List<ArticleTag> { tag1 }
};
Article a2 = new Article
{
Title = "Title12",
Date = DateTime.Now,
Anotation = "Anotation2",
Body = "article_2",
ArticleTags = new List<ArticleTag> { tag2, tag3 }
};
var articles = new List<Article> { a1, a2 };
articles.ForEach(a => context.Articles.Add(a));
context.SaveChanges();
}
}
I can't wrap my mind around this issue and haven't found the correct search keys yet:
I would like to have several categories of items in which all items have specific attributes. Those attributes (text fields, dropdowns, or checkboxes) should be added to a category and I want to edit and save those attributes for each item.
I'm working with MVC 4 and code-first EF5. How can I implement this?
My first approach were several classes like Text, Dropdown that were inherited from an abstract Attribute class and a Category class like this:
public class Category
{
[Key]
public int CategoryId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Item> Items { get; set; }
public virtual ICollection<Attribute> Attributes { get; set; }
}
But then I had no idea to proceed. Am I on the right way or completely wrong? Can someone give me a few hints I can search for?
Edit
Ultimately I'm trying to build a list of hifi devices. Speakers have different attributes than amplifier and those have different attributes to tape recorders. I would like to give a unified look for the details of each device and pre-define specific attributes to that category with an additional free-for-all text area. Speaker XYZ is my item, Speaker my category and dB an attribute.
Ok so this question is basically about the data design.
First, I assume that the rule is:
One item has one category
One category has many attributes
One item has many attributes associated with the category
For rule no.1, it is good enough in your design. (simplified example)
public class Category{
public IEnumerable<Item> Items{get;set;}
}
public class Item{
public Category Category{get;set;}
}
Its clear enough.
For rule no.2, I think you should make a CategoryAttribute class. It holds the relation between one to many Category and Attribute. Basically, CategoryAttribute is a master, whereas the children will be ItemAttribute.
public class Category{
public IEnumerable<CategoryAttribute> CategoryAttributes{get;set;}
}
public class CategoryAttribute{
public Category Category{get;set;}
public string CategoryName{get;set;}
public string DefaultValue{get;set;} // maybe a default value for specific
// attribute, but it's up to you
public IEnumerable<ItemAttribute> ItemAttributes{get;set;}
}
The IEnumerable<ItemAttribute> is the one to many relation between category attribute and item attribute.
For rule no.3, the the ItemAttribute described in rule no.2 will be represented attribute owned by each item.
public class Item{
public IEnumerable<ItemAttribute> ItemAttributes{get;set;}
}
public class ItemAttribute{
public Item Item {get;set;} // it owned by one attribute
public CategoryAttribute{get;set;} // it owned by one category attribute
}
I don't quite sure about how to represent relation or primary and foreign key in code first. Hopefully I can enhance my answer if needed (and if I able). But hopefully my illustration about relations and the class designs for each objects.
I think something like this may work for you...
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Item> Items { get; set; }
public virtual ICollection<Attribute> Attributes { get; set; }
}
public class Item
{
public int ItemId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
public virtual ICollection<ItemAttribute> ItemAttributes { get; set; }
}
public class Attribute
{
public int AttributeId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Category> Categories { get; set; }
public virtual ICollection<ItemAttribute> ItemAttributes { get; set; }
}
public class ItemAttribute
{
public int ItemId { get; set; }
public int AttributeId { get; set; }
public Item Item { get; set; }
public Attribute Attribute { get; set; }
public string Value { get; set; }
public int ValueInt{ get; set; }
// etc.
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ItemAttribute>()
.HasKey(x => new { x.ItemId, x.AttributeId });
modelBuilder.Entity<ItemAttribute>()
.HasRequired(x => x.Item)
.WithMany(x => x.ItemAttributes)
.HasForeignKey(x => x.ItemId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<ItemAttribute>()
.HasRequired(x => x.Attribute)
.WithMany(x => x.ItemAttributes)
.HasForeignKey(x => x.AttributeId)
.WillCascadeOnDelete(false);
// AttributeCategories is created for you - but you can do the same as ^ above to customize
// just change 'ICollection<Category> Categories' to collection of 'ItemAttribute'
}
// use it like e.g.
var item = new Item { Name = "ItemTest", };
var attribute = new Attribute { Name = "attributeTest", };
item.ItemAttributes = new List<ItemAttribute>
{
new ItemAttribute { Item = item, Attribute = attribute, Value = "test", },
};
var category = new Category
{
Name = "cat1",
Items = new[]
{
item,
new Item{ Name = "Item1", },
new Item{ Name = "Item2", },
new Item{ Name = "Item3", },
new Item{ Name = "Item4", },
new Item{ Name = "Item5", },
},
Attributes = new[]
{
attribute,
new Attribute{ Name = "att1", },
new Attribute{ Name = "att2", },
}
};
db.Categories.Add(category);
db.SaveChanges();
var categories = db.Categories.ToList();
ItemAttribute is used to connect and store values.
And you're going to need to further adjust as per your requirements.
I actually never worked with code first approach, but I can give you some idea about how this scenario can be handled...To me, it looks that Item is the major one instead of Category. So you can have this structure...
public class Category
{
[Key]
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public string CategoryDescription { get; set; }
// use attributes here if you want them for Category
//public Dictionary<string, string> ItemnAttributes { get; set; }
}
public class MyItem
{
[Key]
public int ItemId { get; set; }
public string ItemName { get; set; }
public string ItemDescription { get; set; }
public Category ItemnCatagory { get; set; }
public Dictionary<string, string> ItemnAttributes { get; set; }
}
Hope this helps..
I have this code :
public class OrderModel
{
public List<Order> Orders { get; set; }
}
public class Order
{
public string Code { get; set; }
public DateTime CreationDate { get; set; }
public Customer Customer { get; set; }
}
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
I'd like get a List<MyClass> MyClass look like :
public class MyClass
{
public string OrderCode { get; set; }
public string OrderCreationDate { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Automapper can help me for this ? if no other solution to avoid loop ?
Thanks,
To do DTO flattening with automapper looks at this post and also this. They should answer your question.
If you don't want to use automapper I would use a simple Linq. Something like this
var myClassList = (from p in OrderModel.Orders select new MyClass()
{
OrderCode = p.Code,
OrderCreationDate = p.CreationDate,
FirstName = p.Customer.FirstName,
LastName = p.Customer.LastName
}).ToList();
I am trying to get to grips with EF4 CTP5. I have two classes that have a many-to-many relationship: Member and MemberGroup. CTP5 Code First generated two tables (Members and MemberGroups) and also a third named MemberGroupMembers that has two columns (MemberGroupId and MemberId) So far everything is as I was expecting it to be. I have seeded the database with some Members and MemberGroups. The problem is that I cannot find how to assign one or more MemberGroups to a Member, which would result in inserting a row into the MemberGroupMembers table for each MemberGroup that the Member is assigned to.
public class Member
{
public int Id { get; set; }
public Guid SecureId { get; set; }
public DateTime JoinedOn { get; set; }
public virtual ICollection<MemberGroup> MemberGroups { get; set; }
}
public class MemberGroup
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreatedOn { get; set; }
public virtual ICollection<Member> Members { get; set; }
}
public class CTP5testContext : DbContext
{
public CTP5testContext() : base("CTP5test") { }
public DbSet<Member> Members { get; set; }
public DbSet<MemberGroup> MemberGroups { get; set; }
}
public class CTP5testContextInitializer : DropCreateDatabaseIfModelChanges<CTP5testContext>
{
protected override void Seed(CTP5testContext context)
{
new List<Member>
{
new Member
{
Id = 1,
SecureId = Guid.NewGuid(),
JoinedOn = DateTime.Now
}
,
new Member
{
Id = 2,
SecureId = Guid.NewGuid(),
JoinedOn = DateTime.Now
}
}.ForEach(m => context.Members.Add(m));
var memberGroup = new MemberGroup()
{
Id = 1,
Name = "MemberGroup 1",
CreatedOn = DateTime.Now
};
context.MemberGroups.Add(memberGroup);
// How can I assign Member 1 to MemberGroup 1 ?????
context.SaveChanges();
}
}
I hope that it is clear what I am trying to do here and that someone can give me an example of how to achieve this.
Regards,
Erwin
You must use collections defined in your POCO classes. So if you want to assign member1 to memberGroup you will simply call:
memberGroup.Members.Add(member1);