SchemaBuilder with complex types - orchardcms

I want to store complex content part record but couldn't create columns with SchemaBuilder in Migrations file.
Here are my classes:
public enum BoxInheritance
{
Empty, Inherit, Enter
}
public class BoxSize
{
public string Width { get; set; }
public string Height { get; set; }
}
public class BoxSpace
{
public string Left { get; set; }
public string Right { get; set; }
public string Top { get; set; }
public string Bottom { get; set; }
}
public class BoxPartRecord : ContentPartRecord
{
public virtual BoxSize Size { get; set; }
public virtual BoxSpace Space { get; set; }
public virtual Dictionary<string, BoxInheritance> Inheritances { get; set; }
public BoxPartRecord()
{
Size = new BoxSize();
Space = new BoxSpace();
Inheritances = new Dictionary<string, BoxInheritance>();
}
}
Is it ok to use a content part record like this?
How to create a table for this content part record?

I think this won't work. My suggestion is to use simple types in the record class and complex types in the content part itself (you can do the mapping there).
public class BoxPartRecord
{
public virtual int Width { get; set; }
public virtual int Height { get; set; }
...
}
public class BoxPart : ContentPart
{
public BoxSize Size { get { return new BoxSize {record.Width, record.Height} ...
}

Related

Autmapper nested mapping

I have the following main class:
public class ResearchOutcome
{
public ResearchOutcomeCategory ResearchOutcomeCategory { get; set; }
public string? UniqueIdentifier { get; set; }
}
And the category class is:
public class ResearchOutcomeCategory
{
public int Id { get; set; }
public string Name { get; set; }
public string? Description { get; set; }
}
The View models for above classes are:
public class ResearchOutcomeDetailVm : IMapFrom<ResearchOutcome>
{
public int Id { get; set; }
public virtual ResearchOutcomeCategoryDetailVm ResearchOutcomeCategory { get; set; }
}
public class ResearchOutcomeCategoryDetailVm : IMapFrom<ResearchOutcomeCategory>
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
Now, I have used the following mapping profile:
// First this one
profile.CreateMap<ResearchOutcomeCategory, ResearchOutcomeCategoryDetailVm>();
profile.CreateMap<ResearchOutcome, ResearchOutcomeDetailVm>();
//Then I tried this one
profile.CreateMap<ResearchOutcome, ResearchOutcomeDetailVm>()
.ForMember(o => o.ResearchOutcomeCategory,
cat => cat.MapFrom( o => o.ResearchOutcomeCategory));
But the ResearchOutcomeCategory is always null. Any help would be appreciated.
After digging more, I identified that I was not "Including" the relevant item in the query, hence, the view model was always empty. Pretty dumb on my part :D
Regarding the mapping, if the properties (even complex ones) have the same names, then the mapper will map them automatically. So simply this line worked
profile.CreateMap<ResearchOutcomeCategory, ResearchOutcomeCategoryDetailVm>();
Hope it helps someone

Complex Automapper Configuration

I'm mapping from an existing database to a DTO and back again use Automapper (4.1.1) and I've hit a few small problems.
I have a (simplified) model for the database table:
public class USER_DETAILS
{
[Key]
public string UDT_LOGIN { get; set; }
public string UDT_USER_NAME { get; set; }
public string UDT_INITIALS { get; set; }
public string UDT_USER_GROUP { get; set; }
public decimal UDT_CLAIM_LIMIT { get; set; }
public string UDT_CLAIM_CCY { get; set; }
}
and a DTO object:
public class User
{
public string Login { get; set; }
public string UserName { get; set; }
public string Initials { get; set; }
public string UserGroup { get; set; }
public double ClaimLimit { get; set; }
public string ClaimCurrency { get; set; }
}
I've created a profile
public class FromProfile : Profile
{
protected override void Configure()
{
this.RecognizePrefixes("UDT_");
this.ReplaceMemberName("CCY", "Currency");
this.SourceMemberNamingConvention = new UpperUnderscoreNamingConvention();
this.DestinationMemberNamingConvention = new PascalCaseNamingConvention();
this.CreateMap<decimal, double>().ConvertUsing((decimal src) => (double)src);
this.CreateMap<USER_DETAILS, User>();
}
}
However, it seems that Automapper doesn't like combining this many settings in the config. Even simplifying the models, I can't get
this.RecognizePrefixes("UDT_");
this.ReplaceMemberName("CCY", "Currency");
to work together, and whilst
this.CreateMap<decimal, double>().ConvertUsing((decimal src) => (double)src);
works ok with the models in the test, it fails when using it against a database.
Is there a way to get all this to work together, or should I fall back to using ForMember(). I was really hoping I could get this working as there are a lot of tables in this system, and I'd rather not have to do each one individually.
You will need to extend this for other types, only tested with strings, I have an extension method that does all the work and looks for unmapped properties.
public class USER_DETAILS
{
public string UDT_LOGIN { get; set; }
public string UDT_USER_NAME { get; set; }
public string UDT_INITIALS { get; set; }
public string UDT_USER_GROUP { get; set; }
// public decimal UDT_CLAIM_LIMIT { get; set; }
public string UDT_CLAIM_CCY { get; set; }
}
public class User
{
public string Login { get; set; }
public string UserName { get; set; }
public string Initials { get; set; }
public string UserGroup { get; set; }
//public double ClaimLimit { get; set; }
public string ClaimCurrency { get; set; }
}
public static class AutoMapperExtensions
{
public static IMappingExpression<TSource, TDestination>
CustomPropertyMapper<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
var sourceType = typeof(TSource);
var destinationType = typeof(TDestination);
var existingMaps = Mapper.GetAllTypeMaps().First(x => x.SourceType == sourceType && x.DestinationType == destinationType);
var properties = sourceType.GetProperties();
foreach (var property in existingMaps.GetUnmappedPropertyNames())
{
var similarPropertyName =
properties.FirstOrDefault(x => x.Name.Replace("_", "").Replace("UDT", "").ToLower().Contains(property.ToLower()));
if(similarPropertyName == null)
continue;
var myPropInfo = sourceType.GetProperty(similarPropertyName.Name);
expression.ForMember(property, opt => opt.MapFrom<string>(myPropInfo.Name));
}
return expression;
}
}
class Program
{
static void Main(string[] args)
{
InitializeAutomapper();
var userDetails = new USER_DETAILS
{
UDT_LOGIN = "Labi-Login",
UDT_USER_NAME = "Labi-UserName",
UDT_INITIALS = "L"
};
var mapped = Mapper.Map<User>(userDetails);
}
static void InitializeAutomapper()
{
Mapper.CreateMap<USER_DETAILS, User>().CustomPropertyMapper();
}
}
}

how do i use AutoMapper in ICollation<> Fields

when i use AutoMapper for mapping my ViewModels and get All News, thrown error for me.
Errors...
The following property on Mosque.Core.ViewModels.CategoryViewModel cannot be mapped:
Categories
Add a custom mapping expression, ignore, add a custom resolver, or modify the destination type Mosque.Core.ViewModels.CategoryViewModel.
please help me, thank you
//Models
public class News
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<Category> Categories { get; set; }
public virtual User User { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<News> News { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<News> News { get; set; }
}
//ViewModels
public class NewsViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<CategoryViewModel> Categories { get; set; }
public virtual UserViewModel User { get; set; }
}
public class CategoryViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<NewsViewModel> News { get; set; }
}
public class UserViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<NewsViewModel> News { get; set; }
}
how do i use for select All News?
--Update1--
I used onion architecture in the project and i installed AutoMapper in the Service layer and i want get all news from repository and fill into ViewModels and pass to the UI.
my code in service layer is...
public List<NewsViewModel> GetAll()
{
Mapper.CreateMap<News, NewsViewModel>()
.ForMember(dest => dest.Categories, src => src.MapFrom(p => p.Categories))
.ForMember(dest => dest.User, src => src.MapFrom(p => p.User));
Mapper.AssertConfigurationIsValid();
var viewModels = new List<NewsViewModel>();
foreach (var item in _newsRepository.GetAll())
{
var viewModel = Mapper.Map<News, NewsViewModel>(item);
viewModels.Add(viewModel);
}
return viewModels;
}
You don't seem to have created maps for Catagory and User.
Add the following maps:
Mapper.CreateMap<User, UserViewModel>();
Mapper.CreateMap<Category, CategoryViewModel>();
By the way, why are you creating the maps inside the GetAll method? You can create the maps once, usually at application startup.

Why does this ploymorphic List property go null when adding additional objects to the list?

Consider the following:
public class VideoContainer<T>
{
public string Name { get; set; }
//public List<VideoContainer<T>> VideoContainers { get; set; }
}
public class Perspective : VideoContainer<Perspective>
{
public List<VideoContainer<SourceContainer>> VideoContainers { get; set; }
}
I want to ensure VideoContainer<Perspective>.VideoContainers can only contain VideoContainer<SourceContainer> types.
I add a new Perspective object to a List<Perspective> with three VideoContainers. The problem is that when I add a new Perspective to the list, the previously-added Perspective.VideoContainers is null.
Why is this happening?
It sounds like you need two generic types:
public class VideoContainer<T, U>
{
public string Name { get; set; }
public List<VideoContainer<U>> VideoContainers { get; set; }
}
public class Perspective : VideoContainer<Perspective, SourceContainer>
{
// No longer declare the list, just use it... it's now:
// public List<VideoContainer<SourceContainer>> VideoContainers { get; set; }
}

generic interfaces/classes and inheritance

I think I may be using generic interfaces inappropriately (but not sure so please tell me). I have a small inheritance hierarchy for horse racing. There are 3 primary interfaces : IMeeting + IRace + IRunner which I have reduced for the example. The meeting contains races which contains runners. I have used generics to make runtime decisions on the concrete types but it looks ugly, the WriteData method param has to declare the type for IMeeting which has to declare the type for IRace etc e.g.
static void WriteData(IMeeting<IRace<IRunner, string>> meeting)
Here is the little example:
class Program
{
static void Main(string[] args)
{
IMeeting<IRace<IRunner, string>> meeting = new Meeting<IRace<IRunner, string>>();
IRace<IRunner, string> slrace = new SL_Race<IRunner, string>();
IRunner slrunner = new SL_Runner();
slrace.Runners.Add(slrunner);
meeting.Races.Add(slrace);
WriteData(meeting);
}
static void WriteData(IMeeting<IRace<IRunner, string>> meeting)
{
// Write to db or whatever
}
}
public interface IMeeting<T_Race>
{
string Course { get; set; }
string CourseId { get; set; }
List<T_Race> Races { get; set; }
}
public class Meeting<T_Race> : IMeeting<T_Race>
{
public string Course { get; set; }
public string CourseId { get; set; }
public List<T_Race> Races { get; set; }
public Meeting()
{
Races = new List<T_Race>();
}
}
public interface IRace<T_Runner, T_Going>
{
T_Going Going { get; set; }
List<T_Runner> Runners { get; set; }
}
public interface ISL_Race<T_Runner, T_Going> : IRace<T_Runner, T_Going>
{
// Extended behaviour
string Time { get; set; }
string RaceId { get; set; }
string Info { get; set; }
uint MaxOR { get; set; }
}
public class SL_Race<T_Runner, T_Going> : ISL_Race<T_Runner, T_Going>
{
// IRace
public T_Going Going { get; set; }
public List<T_Runner> Runners { get; set; }
// ISL_RACE
public string Time { get; set; }
public string RaceId { get; set; }
public string Info { get; set; }
public uint MaxOR { get; set; }
public SL_Race()
{
Runners = new List<T_Runner>();
}
}
public interface IRunner
{
string Name { get; set; }
}
public class SL_Runner : IRunner
{
public string Name { get; set; }
}
In my real world app there are a few different types of concrete runner and races. I am trying to create a relevant meeting at runtime. In my mind IMeeting must have a declaration for IRaces but the concrete race type can't be known until runtime and same for the runners. My real world app also has more generic parameters and I end up with ugly looking method signatures that have to be aware of the types up the hierarchy e.g.
public List<IMeeting<IRP_Race<IRP_Runner, Going>>> ExtractMeetingList(String dayResultPage)
So am I using generics inappropriately? I could remove all generics by moving the generic properties down to concrete classes and specify them as non generic e.g. I could move IRace.Runners to the SL_Race class, but it seems to me it should be in IRace as a race interface should have runners.
Thanks for any input.
**edit - having thought about it I should probably remove the generics and just create a subclass that has the required types that will be known at compile time e.g.
public interface IMeeting
{
string Course { get; set; }
string CourseId { get; set; }
}
// This is the new subclass with the list of concrete races "ISL_RACE"
public class ISL_Meeting : IMeeting
{
List<ISL_Race> Races { get; set; }
}
public class SL_Meeting : ISL_Meeting
{
public string Course { get; set; }
public string CourseId { get; set; }
List<ISL_Race> Races {get; set;}
public SL_Meeting()
{
Races = new List<ISL_Race>();
}
}

Resources