Alternative to "Relational().ColumnType" in C# - relational

It is used to determine the size of the field if it is not defined before
foreach (var property in modelBuilder.Model.GetEntityTypes()
.SelectMany(e => e.GetProperties()
.Where(p => p.ClrType == typeof(string))))
property.Relational().ColumnType = "varchar(100)";
Error message: 'IMutableProperty' does not contain a definition for 'Relational' and no accessible extension method 'Relational' accepting a first argument of type 'IMutableProperty' could be found (are you missing a using directive or an assembly reference?)

Replace the Relational().ColumnType command with SetColumnType()
foreach (var property in modelBuilder.Model.GetEntityTypes()
.SelectMany(e => e.GetProperties()
.Where(p => p.ClrType == typeof(string))))
property.SetColumnType("varchar(100)");
Depending on the failure check if it called the Linq library
using System.Linq;

Related

Automapper walking down relationships

I'm still trying to wrap my head around how automapper works. I have the EF Core query below, which I'd like to change to using automapper.
var query = from t in Context.Tririga.AsNoTracking()
let l = t.Building
let m = t.Owner
let o = m.Organization
where o.Active
select new MetricBadLabManagerByOrganizationDTO {
CampusName = l.CampusName,
Email = m.Email,
Name = m.Name,
OrgLevel3 = o.ThreeName,
OrgLevel4 = o.FourName,
OrgLevel5 = o.FiveName,
OrgLevel6 = o.SixName,
OrgLevel7 = o.SevenName,
Reason = m.Active == false ? "Inactive Employee" : "Invalid Employee",
SiteName = l.SiteName,
Wwid = m.Wwid
};
return await query.ToArrayAsync();
I'm not sure how to setup a mapper configuration to the DTO type because I can't just go from Tririga to MetricBadLabManagerByOrganizationDTO as it doesn't know how to go down the relationships.
Here is the Getting Started Guide from AutoMapper if you haven't gone through the documentation.
I've recently got a chance to work on a project that uses AutoMapper to translate between persistence models and domain models, and here would be how I set things up:
There are many ways to configure your mappings. I like the Profile Instances method:
using AutoMapper;
namespace Company.Product.Infrastructure.Mapping.AutoMapper
{
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Tririga, MetricBadLabManagerByOrganizationDTO>()
.ForMember(dest => dest.CampusName,
opts => opts.MapFrom(src => src.Building.CampusName))
.ForMember(dest => dest.Email,
opts => opts.MapFrom(src => src.Owner.Email))
.ForMember(dest => dest.Name,
opts => opts.MapFrom(src => src.Owner.Name))
...
}
}
}
There is a whole section about flattening on AutoMapper documentation.
Just a note,
When you configure a source/destination type pair in AutoMapper, the configurator attempts to match properties and methods on the source type to properties on the destination type. If for any property on the destination type a property, method, or a method prefixed with “Get” does not exist on the source type, AutoMapper splits the destination member name into individual words (by PascalCase conventions).
So you might not need to define the rule for each single property you want to map, otherwise what's the point of using AutoMapper.
For example, if your MetricBadLabManagerByOrganizationDTO campus name were named as BuildingCampusName, AutoMapper would be smart enough to look for Building property on your source and see if there is a property called CampusName inside.
There are just lots of valuable information in AutoMapper documentation you can find and learn from, which is what I like!

Using string.Split() in AutoMapper issue

I have an ASP .Net core application. I am simply trying to have my AutoMapper configure to convert a string comma delimited into a list of strings as per this configuration:
configuration.CreateMap<Job, JobDto>()
.ForMember(dto => dto.Keywords, options => options.MapFrom(entity => entity.Keywords.Split(',').ToList()))
For some reason it does not get compiled and give me the following error:
An expression tree may not contain a call or invocation that uses
optional argument
I can't see why I am getting this error. I am pretty sure that I have done that in my other projects before without any such error.
As error says, Split function has an optional parameter. The full signature of it is as this (options is optional)
public string[] Split(string separator, StringSplitOptions options = StringSplitOptions.None)
As you are trying to use a function with default value inside an expression tree, it gives you the error.
To Fix it, easy, just pass on optional parameters by yourself. ( StringSplitOptions.None )
So, simply change it to this:
entity.Keywords.Split(',' , StringSplitOptions.None).ToList()
This is completely true.
Error is raised because expression tree being created is about to contain some more complex logic, like .Split(',').ToList(), which is not an accessible property or method, only top-level reflected object properties and methods are supported (like in class MemberInfo).
Property chaining, deep-calls (.obj1property.obj2property), extension methods are not supported by the expression trees, like in this .ToList() call.
My solution was like this:
// Execute a custom function to the source and/or destination types after member mapping
configuration.CreateMap<Job, JobDto>()
.AfterMap((dto,jobDto)=>jobDto.Keywords = dto.Keywords.Split(',').ToList());
I had the same problem. I do not know if it is an issue or not. Anyway, I found a workaround.
CreateMap<Category, GetCategoryRest>()
.ForMember(dest => dest.Words,
opt => opt.MapFrom(src => ToWordsList(src.Words)));
private static List<string> ToWordsList(string words)
{
return string.IsNullOrWhiteSpace(words) ? new List<string>() : words.Split(",").ToList();
}
It is guaranteed that AutoMapper has always a List. Still, I'm confused. In my Startup.cs I define that AutoMapper allows null values for list.
Mapper.Initialize(cfg => {
cfg.AllowNullCollections = true;
}
Category.Words is a string.
GetCategoryRest.Words is a List<string>
AutoMapper Version: 8.1.1,
AutoMapper.Microsoft.DependencyInjection: 6.1.1
Use .AfterMap
CreateMap<src, dto>()
.ForMember(src =>src.Categories,options=> options.Ignore())
.AfterMap((src, dto) => { dto.Categories.AddRange(src.Categories.Split(",").ToList()); })
.ReverseMap()
.ForMember(src => src.Categories, option => option.MapFrom(dto => string.Join(",", dto.Categories)));

Deconstruction in foreach over Dictionary

Is it possible in C#7 to use deconstruction in a foreach-loop over a Dictionary? Something like this:
var dic = new Dictionary<string, int>{ ["Bob"] = 32, ["Alice"] = 17 };
foreach (var (name, age) in dic)
{
Console.WriteLine($"{name} is {age} years old.");
}
It doesn't seem to work with Visual Studio 2017 RC4 and .NET Framework 4.6.2:
error CS1061: 'KeyValuePair' does not contain a definition for 'Deconstruct' and no extension method 'Deconstruct' accepting a first argument of type 'KeyValuePair' could be found (are you missing a using directive or an assembly reference?)
If you don't like having to write the Deconstruct method, especially if you only need it in one place, here's how to do it as a one-liner with LINQ:
Using your original dictionary:
var dic = new Dictionary<string, int>{ ["Bob"] = 32, ["Alice"] = 17 };
You can do it like this:
foreach (var (name, age) in dic.Select(x => (x.Key, x.Value)))
{
Console.WriteLine($"{name} is {age} years old.");
}
First you have to add an extension method for KeyValuePair:
public static void Deconstruct<T1, T2>(this KeyValuePair<T1, T2> tuple, out T1 key, out T2 value)
{
key = tuple.Key;
value = tuple.Value;
}
Then you will get a different error:
error CS8179: Predefined type 'System.ValueTuple`2' is not defined or imported
According to this answer you have to install the NuGet package System.ValueTuple.
Then it should compile. However Visual Studio 2017 RC4 will say that it cannot resolve the symbol names name and age. They should hopefully fix this in a future update.
Deconstruct of KeyValuePair<TKey,TValue> is implemented in .NET Core 2.0, but not in .NET Framework (up to 4.8 preview) unfortunately.

MVC 5 Custom Html Helper extension issue

I'm trying to create a custom html helper class.
I have the below as a very simple start, complies fine:
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
namespace Mobile.HtmlHelpers
{
public static class RequestBox
{
public static HtmlString CascadeBoxFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, object htmlAttributes = null)
{
HtmlString html = (HtmlString)HtmlHelperInputExtensions.TextBoxFor(helper, expression);
return html;
}
// IHtmlContent html = HtmlHelperInputExtensions.TextBoxFor(htmlHelper, expression);
}
}
I am trying to call it and the system doesn't like it..
I can see it if I use:
#foreach (var item in Model.RequestModel.Requests)
{
#RequestBox.CascadeBoxFor(x=>item.EmployeeDescription)
}
But I get:
Severity Code Description Project File Line Suppression State
Error CS7036 There is no argument given that corresponds to the required formal parameter 'expression' of 'RequestBox.CascadeBoxFor<TModel, TValue>(HtmlHelper, Expression<Func<TModel, TValue>>, object)' Mobile..NET Framework 4.6.1
If I try suggestions of it being an extension and I should be able to use #Html.CascadeBoxFor(x => item.EmployeeDescription), I get:
Severity Code Description Project File Line Suppression State
Error CS1061 'IHtmlHelper<RequestPageModel>' does not contain a definition for 'CascadeBoxFor' and no extension method 'CascadeBoxFor' accepting a first argument of type 'IHtmlHelper<RequestPageModel>' could be found (are you missing a using directive or an assembly reference?) Mobile..NET Framework 4.6.1
Can anyone tell me what is missing here?

ServiceStack Ormlite Select Expression

I am building a service using ServiceStack and using OrmLite to communicate with database. I found following example in ServiceStack OrmLite Documention:
db.Select<Author>(q => q.Earnings <= 50);
OR
db.Select<Author>(q => q.Name.StartsWith("A"));
I am trying it with my class User, but unable to find a overload for method "Select" which allows me to do mentioned stuff. In my case q is a linq expression not an instance/reference for generic class type (User in my case). Following is my code:
db.Select<User>(q => q.Where(x => x.LastName == "XYZ"));
and i want it to be like:
db.Select<User>(q => q.LastName == "XYZ");
Please let me know if that is an extension method which i am looking for and how can i use that?
The Type that gets selected is the table is looking at, e.g:
db.Select<Author>(...) //Author
db.Select<User>(...) //User
See the answers on this earlier question for selecting a subset of data with OrmLite.

Resources