Dbgeography in .net standard 2.0 - asp.net-core-2.0

I want to use asp .net core v2 web api service to do some spatial calculations. I believe that this is not possible because of the lack of support in Net standard 2.0 for dbgeography spatial type in .net standard 2.0. Is thre any workaround for this at the moment until such time as dbgeography or it's equivalent is suported?

I wanted to comment but I can't yet. Try checking out this post, it shows a turn around for using spatial operations: System.Data.Entity.Spatial replacement in ASP.NET Core

After trying different libraries i ended up making my own class. I would like to share it with you guys, in order to optimize it.
- Scenario : Need to get distance between User & Store
- Store Class :
public class Store
{
public int Id { get; set; }
public string Description { get; set; }
public string Name { get; set; }
public Location Location { get; set; }
}
- Location Class :
public class Location
{
public DataContext _dbContext { get; set; }
public Location()
{
}
public Location(DataContext dbContext)
{
_dbContext = dbContext;
}
public double Longitude { get; set; }
public double Latitude { get; set; }
public double Distance(Location destination, int srid=4326)
{
var source = this;
var Distance = string.Empty;
var query = #"DECLARE #target geography = geography::Point(" + destination.Latitude + #"," + destination.Longitude + #"," +srid+#")
DECLARE #orig geography = geography::Point(" + source.Latitude + #"," + source.Longitude + #"," + srid + #")
SELECT #orig.STDistance(#target) as Distance";
try
{
var dbConn = _dbContext.Database.GetDbConnection();
dbConn.Open();
var command = dbConn.CreateCommand();
command.CommandType = System.Data.CommandType.Text;
command.CommandText = query;
return Convert.ToDouble(command.ExecuteScalar().ToString());
}
catch (Exception ex)
{
Error.LogError(ex);
throw ex;
}
}
}
Make sure to add data annotation [NotMapped] on Location Property in Store Class or add follwoing line in your Data Context Class :
modelBuilder.Entity<Store>().OwnsOne(c => c.Location);
And use it like this
Location loc = new Location(_dbContext);
var store = _dbContext.Store.FirstOrDefault();
loc.Longitude = 55.22;
loc.Latitude = 33.55;
var distance = store.Location.Distance(loc);
In case of help you can contact me anytime.

Related

.Net Core 2 FromSql issue

Hello I'm starting to learn how to use .Net Core. I've been trying to get the .FromSql() to work with one of my queries. The issue I'm having is that when the query hits the server #metric is getting set to #metricID=N'"100"' when I set #metricID with query.MetricId. I used the sql profiler to see why I kept getting nothing back and it looks something like this.
exec sp_executesql N' select * from metrics where rr.metric_id = #metricID',N'#metricID nvarchar(5)',#metricID=N'"100"'
public async Task<List<MetricView>> GetMetricAsync(MetricQuery query)
{
string qry = #"select * from metrics where metricid = #metricID";
SqlParameter metricID = new SqlParameter("#metricID", query.MetricId);
try
{
var metricReturn = await _context.Metrics
.FromSql(qry, metricID).ToListAsync();
return _mapper.Map<List<MetricView>>(metricReturn);(dropdown);
}
catch (Exception e)
{
var t = e.Message;
return null;
}
finally
{
_sqlConnection.Dispose();
}
}
public class MetricQuery
{
public string MetricId { get; set; }
public string MircoRegion { get; set; }
public string Consortia { get; set; }
public string Institution { get; set; }
public string AcademicYear { get; set; }
}
The issue I'm having is that when the query hits the server #metric is getting set to #metricID=N'"100"'
For this issue, it is caused by op adding extra double quotes in postman.
Removing it from postman should resolve this issue.

Automapper: map an anonymous/dynamic type

I need some help to map an anonymous object using Automapper. The goal is combine Product and Unity in a ProductDto (in which unity is a product's property).
Autommaper CreateMissingTypeMaps configuration is set to true.
My Classes:
public class Product
{
public int Id { get; set; }
}
public class Unity
{
public int Id { get; set; }
}
public class ProductDto
{
public int Id { get; set; }
public UnityDto Unity{ get; set; }
}
public class UnityDto
{
public int Id { get; set; }
}
Test Code
Product p = new Product() { Id = 1 };
Unity u = new Unity() { Id = 999 };
var a = new { Product = p, Unity = u };
var t1 = Mapper.Map<ProductDto>(a.Product);
var t2 = Mapper.Map<UnityDto>(a.Unity);
var t3 = Mapper.Map<ProductDto>(a);
Console.WriteLine(string.Format("ProductId: {0}", t1.Id)); // Print 1
Console.WriteLine(string.Format("UnityId: {0}", t2.Id)); // Print 999
Console.WriteLine(string.Format("Anonymous ProductId: {0}", t3.Id)); // Print 0 <<< ERROR: It should be 1 >>>
Console.WriteLine(string.Format("Anonymous UnityId: {0}", t3.Unity.Id)); // Print 999
There are two maps added to the profile:
CreateMap<Product, ProductDto>();
CreateMap<Unity, UnityDto>();
The problem is how Automapper map anonymous objects. I haven't time to check out Automapper source code but I got the desired behaviour with minor changes on my anonymous object:
var a = new { Id = p.Id, Unity = u };
By doing this, I might even delete previous mappings because now it is using only CreateMissingTypeMaps.
Note: As matter of fact I'm not sure if it is really an issue or I it was just my unreal expectations.

Entity Framework Codefirst approach

Am Using EntityFramework codefirst approach.my coding is
class Blog
{
[Key]
public int BlobId { get; set; }
public string Name { get; set; }
public virtual List<Post> Posts { get; set; }
}
class Post
{
[Key]
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlobId { get; set; }
public virtual Blog Blob { get; set; }
}
class BlogContext:DbContext
{
public BlogContext() : base("constr") { }
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
class Program
{
static void Main(string[] args)
{
using (var db = new BlogContext())
{
Console.WriteLine("Enter a name for a new blob:");
var name = Console.ReadLine();
var b = new Blog { Name = name };
db.Blogs.Add(b);
db.SaveChanges();
Till this step i created two tables(Blogs and Posts)in my SQlserver.The BlobId is primary key in Blogs table.and foreign key in Posts table.and Blogid in blog table is auto incremented.postid in posts table is also auto incremented
class Program
{
static void Main(string[] args)
{
using (var db = new BlogContext())
{
Console.WriteLine("Enter a name for a new blob:");
var name = Console.ReadLine();
var b = new Blog { Name = name };
db.Blogs.Add(b);
db.SaveChanges();
Here i added name in the blogtable
var id1 = from val in db.Blogs
where val.Name == name
select val.BlobId;
Now by using Name am obtaining the blogid of blogs table
Console.WriteLine("Enter Title:");
var title = Console.ReadLine();
Console.WriteLine("Enter Content");
var content = Console.ReadLine();
var c = new Post { Title = title, Content = content, BlobId = id1};
db.Posts.Add(c);
db.SaveChanges();
here am reading the data for title,content.Then adding the title,content and blogid(which i obtained from another table) into Posts table
I getting error at BlobId = id1
Am getting Cannot implicitly convert type 'System.Linq.IQueryable' to 'int' this error
}
Console.ReadLine();
}
}
Can you help me to solve this.If you did not understand what i explained please reply me
The following query is a sequence of elements, not a scalar value, even though you believe that there is only one result, it is still a collection with one element when the results of the query are iterated over:
var id1 = from val in db.Blogs
where val.Name == name
select val.BlobId;
Change this to:
int id1 = (from val in db.Blogs
where val.Name == name
select val.BlobId).First();
This query will execute immediately and return the first element in the sequence. It will throw an exception if there is no match, so you may want to use FirstOrDefault and assign to a nullable int instead.

Automapper map custom collections

Hello. I have a list that looks like this one:
public class PagedList<T> : List<T>
{
public PagedList(IEnumerable<T> collection) : base(collection)
{ }
public int TotalItems { get; set; }
public int CurrentPage { get; set; }
public int PageSize { get; set; }
//some other properties
}
and used in repository for paging
public PagedList<TEntity> GetPaged(int page)
{
var pagedEntities = some_query;
return pagedEntities.AsPagedList(totalResults, page, pageSize);
}
The same PagedList is also used in asp mvc view models for paging.
Is it possible to map this collections using Automapper with all the properties TotalItems/CurrentPage/... ?
PagedList<DbItem> dbItems = _repository.GetPages(page);
var viewItems = new PagedList<SomeItemView>();
Mapper.Map(dbItems , viewItems);
Tahnk You !
This worked for me. Are you looking for something more generic?
public class DbItem
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ViewItem
{
public int Id { get; set; }
public string Name { get; set; }
}
public class PagedList<T>: List<T>
{
public int TotalItems { get; set; }
public int CurrentPage { get; set; }
public int PageSize { get; set; }
}
class Program
{
static void Main(string[] args)
{
MapItems();
}
public static void MapItems()
{
Mapper.CreateMap<DbItem, ViewItem>();
Mapper.CreateMap<PagedList<DbItem>, PagedList<ViewItem>>()
.AfterMap((s, d) => Mapper.Map<List<DbItem>, List<ViewItem>>(s, d));
var dbList = new PagedList<DbItem>
{
new DbItem {Id = 1, Name = "a"},
new DbItem {Id = 2, Name = "b"}
};
dbList.TotalItems = 2;
dbList.CurrentPage = 1;
dbList.PageSize = 10;
var viewList = Mapper.Map<PagedList<DbItem>, PagedList<ViewItem>>(dbList);
Console.WriteLine(viewList.TotalItems);
Console.WriteLine(viewList.CurrentPage);
Console.WriteLine(viewList.PageSize);
Console.WriteLine(viewList[0].Id + " " + viewList[0].Name);
Console.WriteLine(viewList[1].Id + " " + viewList[1].Name);
Console.ReadLine();
}
}
What you need is a custom type converter
public class PagedListConverter<TIn, TOut> : ITypeConverter<IPagedList<TIn>, IPagedList<TOut>>
{
public IPagedList<TOut> Convert(AutoMapper.ResolutionContext context)
{
var source = (IPagedList<TIn>)context.SourceValue;
var mapped = Mapper.Map<IList<TOut>>(source);
return new StaticPagedList<TOut>(mapped, source.GetMetaData());
}
}
Usage:
Mapper.CreateMap<IPagedList<Company>, IPagedList<CompanyViewModel>>().ConvertUsing<PagedListConverter<Company, CompanyViewModel>>();
For those who have faced the similar problem recently, and as an update to NoPyGod's answer, you can achieve the general mapping using ITypeConverter. According to the official documentation:
AutoMapper also supports open generic type converters with any number of generic arguments:
var configuration = new MapperConfiguration(cfg =>
cfg.CreateMap(typeof(Source<>), typeof(Destination<>)).ConvertUsing(typeof(Converter<,>)));
The closed type from Source will be the first generic argument, and the closed type of Destination will be the second argument to close Converter<,>.
So the custom type converter would be:
private class Converter<TSource, TDestination>
: ITypeConverter<PagedList<TSource>, PagedList<TDestination>>
{
public PagedList<TDestination> Convert(
PagedList<TSource> source,
PagedList<TDestination> destination,
ResolutionContext context) =>
new PagedList<TDestination>(
context.Mapper.Map<List<TSource>, List<TDestination>>(source));
/* Additional settings comes here. */
}
And then register it:
this.CreateMap(typeof(PagedList<>), typeof(PagedList<>)).ConvertUsing(typeof(Converter<,>));

Deserialization of XDocument doesn't work

I have this code:
[Serializable]
[XmlRoot("ISO_CCY_CODES")]
public class IsoCurrencyCodes
{
public IsoCurrencyCodes()
{
IsoCodes = new List<IsoCurrencyCode>();
}
public IsoCurrencyCodes(List<IsoCurrencyCode> isoCodes)
{
IsoCodes = isoCodes;
}
[XmlArrayItem("ISO_CURRENCY")]
public List<IsoCurrencyCode> IsoCodes { get; set; }
public static IEnumerable<IsoCurrencyCode> Get()
{
var doc = XDocument.Parse(XmlStringIsoCodes.XmlSource.Replace("\r\n", ""));
var res = doc.Deserialize<IsoCurrencyCodes>();
return res.IsoCodes;
}
}
[Serializable]
[XmlRoot("ISO_CURRENCY")]
public class IsoCurrencyCode
{
public IsoCurrencyCode()
{
}
[XmlElement(ElementName = "ENTITY")]
public string Entity { get; set; }
[XmlElement(ElementName = "CURRENCY")]
public string Currency { get; set; }
[XmlElement(ElementName = "ALPHABETIC_CODE")]
public string Alpha_Code3 { get; set; }
[XmlElement(ElementName = "NUMERIC_CODE")]
public int NumCode { get; set; }
[XmlElement(ElementName = "MINOR_UNIT")]
public string MinorUnit { get; set; }
}
And this code, for deserialization:
public static XDocument Serialize<T>(this T source)
{
var ser = new XmlSerializer(source.GetType());
var sb = new StringBuilder();
using (var writer = new StringWriter(sb))
{
ser.Serialize(writer, source);
}
return XDocument.Parse(sb.ToString());
}
public static T Deserialize<T>(this XDocument xmlDocument)
{
var xmlSerializer = new XmlSerializer(typeof (T));
using (var reader = xmlDocument.CreateReader())
return (T) xmlSerializer.Deserialize(reader);
}
This is the XML source
But deserialization doesn't work. Please help.
Thanks!
I believe you only want to use XMLArray if you have a collection element for all of the items to sit underneath. For example here it could be ISO_CURRENCIES. I'm assuming you can't change the source in this case, so just use this instead:
[XmlElement("ISO_CURRENCY")]
public List<IsoCurrencyCode> IsoCodes { get; set; }
You should find that works.
Additionally, if you find you have further problems in getting the deserialization classes right, you can have them autogenerated for you from the XML and then you can take a look at the code that is created:
xsd source.xml
xsd source.xsd /c
This will create source.cs which you can then use in your project or adapt for your own uses.
As a further note, you'll find you can't use int for Minor_Unit as it's nullable (look at ANTARCTICA). You can't deserialize straight to an int?, so you'll either have to make it a string or go via another property, look at this question for more information.

Resources