Is it possible to set an attribute to specific tables/classes in EF and then query a list of those tables out based on that attribute?
Add the attribute the their partial classes. Then get a list of types with that attribute in the current assembly
Assembly thisAsm = Assembly.GetExecutingAssembly();
var tableTypes = thisAsm.GetTypes()
.Where(t => t.IsDefined(typeof(MyAttribute), false));
Ok, I think I got it working without using Assembly:
public class FoodAttribute : Attribute { }
public class Fruit { }
public class Coin { }
public class Cereal { }
public class FooContext : DbContext {
[Food]
public virtual DbSet<Fruit> Fruits { get; set; }
public virtual DbSet<Coin> Coins { get; set; }
[Food]
public virtual DbSet<Cereal> Cereals { get; set; }
}
To get a list of tables with the attribute Food:
var tableList = typeof(FooContext).GetProperties()
.Where(n => n.IsDefined(typeof(ClientTraitAttribute)))
.Select(n => n.PropertyType.GetGenericArguments()[0].Name).ToList();
Thanks Yuriy for showing me the IsDefined method! :D
Related
I am using MVC5 Code First and have a couple of classes that look like;
public class Asset
{
public int Id { get; set; }
public string FileName { get; set; }
public virtual ICollection<Category> Categories { get; set; }
}
public class Category
{
public int CategoryId { get; set; }
public int AssetId { get; set; }
public string CategoryName { get; set; }
public string Description { get; set; }
}
I would like to return a view that lists all Assets that have a particular Category. I was hoping for something along the lines of
public ActionResult ListByCategory(string categoryName)
{
var model =
from r in _db.Assets
.Where(r => r.Categories.CategoryName == categoryName)
select r;
return View(model);
}
I know I have some assets from my seed method that have categories that exist. But the compiler is saying "System.Collection.Generic.ICollection Does not contain a definition for CategoryName and no extension method could be found, am I missing a reference?" This is on my .Where line.
I don't fully understand what it's trying to tell me. I do have a reference to my Models as I can reference them elsewhere within the controller. I know that a single Asset might be in several Categories, hence I created the ICollection at the class level.
Here is my context class;
public class AssetsDb : DbContext
{
public AssetsDb() : base("DefaultConnection")
{
}
public DbSet<Asset> Assets { get; set; }
public DbSet<Category> Categories { get; set; }
}
Could somebody help my understanding on how I can get to my underlying data? I'm trying to learn EF / MVC so appreciate any help.
Thanks.
You cannot get a CategoryName from a collection of categories, you need to check the name of each category within the collection.
Try using this query instead:
var model =
from r in _db.Assets
.Where(r => r.Categories.Any(c => c.CategoryName == categoryName))
select r;
Simple question - is there a way to render values from Discriminator column in my db? I have inheritance in my model and there is a Discriminator column but it's not accessible from any view like other columns. The model is generated by code-first and EF6.
You can use SqlQuery<T>, where T is a derived type of base class that is not an EF entity [NotMapped].
public abstract class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
[NotMapped]
public class PersonVm : Person
{
public string Discriminator { get; set; }
}
public class AppContext : DbContext
{
public DbSet<Person> People { get; set; }
}
And use it like this.
var q = db.People.Where(x => x.Name == "Foo").ToString();
var people = db.Database.SqlQuery<PersonVm>(q).ToArray();
// var here = people[0].Discriminator;
I'm getting started with the concept of mapping domain models to view models in ASP.NET MVC after watching a recommendation to do this to pass specific viewModels to the views.
I've been able to manage a basic mapping of one domain model to a simpler viewmodel with less properties but now need to produce a more complex viewmodel and can't figure it out. I have the following domain models
public class Club
{
public int ClubID { get; set; }
public string FullName { get; set; }
public string Description { get; set; }
public string Telephone { get; set; }
public string URL { get; set; }
public DateTime CreatedDate { get; set; }
public virtual ICollection<Member> Members{ get; set; }
}
public class Member
{
public int MemberID{ get; set; }
public string Name { get; set; }
public MemberType Membership{ get; set; }
public virtual Club Club { get; set; }
public virtual int ClubID { get; set; }
}
public enum MemberType
{
Standard,
Special,
Limited
}
I want to map to a view model such as this (note: I've split it like this because I think it makes sense but I'm not sure)...
public class ClubDetailsViewModel
{
public int ClubID { get; set; }
public string FullName { get; set; }
public string Description { get; set; }
public IList<ClubDetailsMemberSummaryViewModel> Members { get; set; }
}
public class ClubDetailsMemberSummaryViewModel
{
public MemberType Membership { get; set; }
public int MemberCount { get; set; }
}
What I'm trying to end up with is a page which displays some of the club details plus a summary report of the member types at the club with a count of the members. Such as:
Some Club Name
Description of the club.....
CLUB MEMBERS
Limited - 15
Standard - 100
So I think the viewmodel makes sense for this (although might be a better way to do it). Where I'm struggling is how to map the elements. I can get the Club to map the main fields to the club viewmodel but really can't work out how to map the result of the list of clubs onto their view model and then add that to the main view model as a list.
I'm getting the clubs from my repository using this
var clubs = _clubRepository.GetClubByID(ID);
Then I can transform the Courts which are returned using an include in the data access layer from entity framework using this
var grpCourts = from c in clubs.Members
group c by c.Membership into grp
select new { st = grp.Key, count = grp.Distinct().Count() };
How would I loop through the resulting records and map those to the ClubDetailsMemberSummaryViewModel and then add the list of those to the main ClubDetailsViewModel?
Your mapping from Club to ClubDetailsViewModel will be trivial with the exception of Members. For that property, you could write a quick resolver inline or write your own custom resolver. An inline resolver would look something like this:
Mapper.CreateMap<Club, ClubDetailsViewModel>()
.ForMember(dest => dest.Members, opt => opt.ResolveUsing(src =>
{
return src.Members
.GroupBy(m => m.Membership)
.Select(grp => new ClubDetailsMemberSummaryViewModel
{
Membership = grp.Key,
MemberCount = grp.Distinct().Count()
});
}));
I think it's good practice to refactor more complex resolvers like this out to their own classes:
public class MembershipClubDetailsResolver : ValueResolver<Club, IList<ClubDetailsMemberSummaryViewModel>>
{
protected override IList<ClubDetailsMemberSummaryViewModel> ResolveCore (Club source)
{
return source.Members
.GroupBy (m => m.Membership)
.Select(grp => new ClubDetailsMemberSummaryViewModel
{
Membership = grp.Key,
MemberCount = grp.Distinct().Count()
})
.ToList();
}
}
And then use that resolver in your mapping:
Mapper.CreateMap<Club, ClubDetailsViewModel>()
.ForMember(dest => dest.Members, opt => opt.ResolveUsing<MembershipClubDetailsResolver>());
Your mapping appears to be rather complex, I think I would use the .ConvertUsing method of automapper
Mapper.CreateMap<List<Club>,List<ClubDetailsViewModel>>()
.ConvertUsing<ClubToClubDetailsViewModel>();
The conversion class has the following inheritance
public class ClubToClubDetailsViewModel: TypeConverter<List<Club>,List<ClubDetailsViewModel>>
{
....
}
Alternatively you can tinker with creating two "simple" mappings
Mapper.CreateMap<Club,ClubDetailsViewModel>()
That will map everything except the property called Members
Then you need to create a mapping for the members to ClubDetailsMemberSummaryViewModel, you can do that mapping manually or you can configure this in automapper aswell.
For more specific details on automapper you can visit https://github.com/AutoMapper/AutoMapper/wiki
How do I map a property from an object to another object with a different property name?
I have a Product class that looks like this:
public class Product : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
And the view model looks like:
public class ProductSpecificationAddViewModel
{
public int ProductId { get; set; }
public string ProductName { get; set; }
}
I need to do the following mapping:
Product.Id => ProductSpecificationAddViewModel.ProductId
Product.Name =>ProductSpecificationAddViewModel.ProductName
Here is my action method:
public ActionResult Add(int id)
{
Product product = productService.GetById(id);
// Mapping
//ProductSpecificationAddViewModel viewModel = new ProductSpecificationAddViewModel();
//viewModel.InjectFrom(product);
return View(viewModel);
}
How would I do this?
If you are using ValueInjecter then you would write a ConventionInjection. See the second sample here
public class PropToTypeProp : ConventionInjection
{
protected override bool Match(ConventionInfo c)
{
return c.TargetProp.Name == c.Source.Type.Name + c.TargetProp.Name;
}
}
this injection will do from all properties of TSource.* to TTarget.TSource+*, so you do:
vm.InjectFrom<PropToTypeProp>(product);
You can do this easily with AutoMapper. By default is uses convention (i.e. Id maps to Id and Name to Name), but you can also define custom mappings.
Mapper.CreateMap<Product, ProductSpecificationAddViewModel>()
.ForMember(destination => destination.ProductName,
options => options.MapFrom(
source => source.Name));
Your contoller mapping code will be then this simple :
Mapper.Map(product, viewModel);
i'd like to know how can I get a property like an entity, for example:
My Model:
public class Product {
public int Id { get; set; }
public string Name { get; set; }
public Category Category { get; set; }
}
View:
Name: <%=Html.TextBoxFor(x => x.Name) %>
Category: <%= Html.DropDownList("Category", IEnumerable<SelectListItem>)ViewData["Categories"]) %>
Controller:
public ActionResult Save(Product product)
{
/// produtct.Category ???
}
and how is the category property ? It's fill by the view ? ASP.Net MVC know how to fill this object by ID ?
Thanks!
This is one of the reasons why it's bad to bind directly to entities. Consider
public class ProductForm {
public int Id { get; set; }
public string Name { get; set; }
public string CategoryId { get; set; }
}
public ActionResult Save(ProductForm form)
{
var product = new Product
{
Id = form.Id,
Name = form.Name,
Category = database.GetCategory(form.CategoryId)
};
}
In case of view models as above, it may be OK to use custom model binders to automatically get entities by Id from database. See here for sample implementation (in S#arp Architecture) that binds IDs to entities from database. But I think for now you better go with simpler implementation like above.
You can also use AutoMapper to simplify form->entity mapping.