Linq and SQL, get only some data - sql

I have two classes like this:
public class Book
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime Date { get; set; }
public int AuthorID { get; set; }
public Author author { get; set; }
}
public class Author
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime BirthDate { get; set; }
public string Place { get; set; }
}
They are linked with Foreign Key (of course AuthorID form Book and ID from Author)
I can retrieve data with this query through LINQ:
var _book = context.Book
.Where(x => x.ID == ID_I_Pass_From_FrontEnd)
.Select(x => new Book {
ID = x.ID,
Name = x.Name,
Date = x.Date,
author = new Author {ID = x.author.ID, Name = x.author.Name})
.FirstOrDefault();
In this way I get data without BirthDate and Place from Author and this is my goal.
Unfortunately I need to use SQL because I'm afraid of performances in case number of data will increase. So I started to code and with this:
var sql = string.Format("SELECT * FROM[Book] AS[x] WHERE([x].ID == ({0})), ID_I_Pass_From_FrontEnd");
var result = context.Book.FromSql(sql).Include(x => x.author).FirstOrDefault();
I can retrieve data but with BirthDate and Place. I'd like to avoid these informations.
Of course my real classes are full of columns, what I wrote in this post is just an example.
Other informations:
I'm using AZURE sql as db
I tried, in sql string, to insert INNER JOIN for join with Author table but I got error: "Sequence contains more than one matching element". Probably it is confused about same column name (ID in Book and ID in Author).
How can I achieve, with SQL string, same data of LINQ query? so how can I avoid to get data not useful in that moment?

Use the same projection as in pure LINQ query. There is no difference:
var _book = context.Book.FromSql(sql)
.Select(x => new Book {
ID = x.ID,
Name = x.Name,
Date = x.Date,
author = new Author {ID = x.author.ID, Name = x.author.Name})
.FirstOrDefault();

Related

Count, order desc and select top 4 values for ASP.NET

I am trying to create a table that shows the top 4 nationalities in my database for my .NET application. I can do it using SQL following this.
This is the result in sql
However, what I need for my application is
I have tried using LINQ as well as var in the View but unable to get it. I saw this and tried following but could not quite understand
Employee Model
public class employees
{
public int eeID { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public string Nationality { get; set; }
}
You can do the below things
Store the Grouped Nationalities sorted as per their counts
Get the top results, use Enumerable.Take
Get the count of the other Nationalities which should not include the top records, For that, you can use Enumerable.Skip
Concat result from points 2 and 3.
Pass the final output to the view.
Sample Code. Please refer to this link for working code
// Your Logic
var topRecordsCount = 4;
var groupedResult = list
.GroupBy(x => x.Nationality)
.OrderByDescending(x => x.Count());
var topRecords = groupedResult
.Take(topRecordsCount)
.Select(x => new FinalResult {Nationality = x.Key, Total = x.Count()}).ToList();
var othersCount = groupedResult.Skip(topRecordsCount).Select(x => x.Count()).Sum();
var othersRecord = new FinalResult { Nationality = "Others", Total = othersCount};
topRecords.Add(othersRecord);
foreach(var top in topRecords)
Console.WriteLine(top.Nationality + " - " + top.Total);
// model to store the final output
public class FinalResult {
public string Nationality { get; set; }
public int Total { get; set; }
}

Transformations Filter

I have the following Map / Transform
public class PositionSearch : AbstractIndexCreationTask<Employer>
{
public PositionSearch()
{
Map = employers =>
from employer in employers
from position in employer.Positions
select new
{
EmployerName = employer.Name,
SearchSkills = position.RequiredSkills
.Select(x => x.Skill)
};
TransformResults = (database, results) =>
from result in results
from position in result.Positions
select new
{
EmployerId = result.Id,
EmployerName = result.Name,
PositionId = position.Id,
PositionTitle = position.Title,
RequiredSkills = position.RequiredSkills
.Select(x => new { x.Skill, x.Proficiency })
};
// Any field you are going to use .Search() on should be analyzed.
Index("SearchSkills", FieldIndexing.Analyzed);
}
}
I have an employer object with two positions, each with a single skill, "NH" and "MVC"
When I execute the following query I'm getting two position results returned, when I expected one.
Can anyone tell me why this is behaving this way? I've got a feeling it's something to do with a join i'm performing, but I'm not sure.
using (var session = DocumentStore.OpenSession())
{
var results = session.Query<PositionSearchResultModel, PositionSearch>()
.Customize(x => x.WaitForNonStaleResults())
.Search(x => x.SearchSkills, "NH")
.OfType<PositionSearchResultModel>().ToList();
Assert.AreEqual(1, results.Count());
}
I'm wanting to use transform so that I can access the Temp-Index-Score meta data for ordering, I've been unable to access the meta data without a transform so far.
You are indexing the Employer document. The search found a document that contained the skill in question, and then you asked to transform the document.
The way you had it before is projecting from the index, which is the only way you are going to get the specific position found as part of your results.
I really think you would be happier with Position as it's own document...
public class Employer
{
public string Id { get; set; }
public string Name { get; set; }
}
public class Position
{
public string Id { get; set; }
public string EmployerId { get; set; }
public string Title { get; set; }
public string Location { get; set; }
public ICollection<SkillProficiency> RequiredSkills { get; set; }
}
This may seem more relational in thinking, but it works just fine in RavenDB, and it will be much easier to query than what you are doing now.

Guid as Id in RavenDB

The RavenDb documentation states:
Numeric or Guid Id properties are supported and will work seamlessly. In this case, RavenDB will automatically make the translation between the inner string ID to the numeric or Guid value shown in the entity and back.
I have stored the following objects:
class A
{
public Guid Id { get; set; }
public Guid BId { get; set; }
}
class B
{
public Guid Id { get; set; }
public string Name { get; set; }
}
I have then created the following projection:
class AB
{
public Guid Id { get; set; } // This should be the Id of A
public Guid BId { get; set; } // This should be the Id of B
public string BName { get; set; } // This should be the name of B
}
I have created the following index to create the projection:
class MyIndex : AbstractIndexCreationTask<AB>
{
public MyIndex()
{
Map = docs =>
from d in docs
select new
{
d.Id,
d.BId,
BName = string.Empty
};
TransformResults = (database, results) =>
from r in results
let b = database.Load<B>("bs/" + r.BId.ToString())
select new
{
r.Id,
r.BId,
BName = b.Name
};
}
}
When I use the following query:
session.Query<AB, MyIndex>().FirstOrDefault(t => t.Id == guid);
I get this exception:
Error converting value "bs/cc0a65ae-dd36-4437-8a57-fa20b91eeef7" to type 'System.Guid'. Path 'Id'.
Questions:
It is caused by the conversion in my projection since the Id is a string there and not my Guid anymore. However, leaving it out will not return the Id. What must I do?
I have to use the string building "bs/" + r.BId.ToString() to load the related doc. Is there a way not having to do this? Is there some sort of function that would resolve the doc tag for me?
Is there a generic way to strip out the document tag altogether?
My constraints.
I will generate the Guid and cannot let RavenDb generate it for me. I know that the Document ID in reality is string, but I really need to use a Guid that I create. I would prefer to own the Id property of my entities.
I'm using Raven.Client 1.0.972
You can achieve this using a MultiMap/Reduce Index, but you will need some hackery:
1) You will need to reduce using strings, not guids. You can still get the values back as guids in your AB class, as I will demonstrate below.
2) You can't call the first property of your AB class "Id", as raven will try to translate it to "__document_id". So call it "AId" and it will work fine.
3) In the mapping phase, you have to manipulate the strings yourself to strip off the document key prefix.
Here's a sample program that puts it all together. This demonstrates that it does indeed work, but I think it also shows why Ayende prefers string identifiers so you don't have to deal with this kind of mess.
using System;
using System.Linq;
using Raven.Client.Document;
using Raven.Client.Indexes;
namespace RavenScratchTest
{
class Program
{
static void Main()
{
var documentStore = new DocumentStore { Url = "http://localhost:8080" };
documentStore.Initialize();
IndexCreation.CreateIndexes(typeof(Program).Assembly, documentStore);
using (var session = documentStore.OpenSession())
{
var b = new B { Id = Guid.NewGuid(), Name = "Foo" };
var a = new A { Id = Guid.NewGuid(), BId = b.Id };
session.Store(a);
session.Store(b);
session.SaveChanges();
}
using (var session = documentStore.OpenSession())
{
var a = session.Query<A>().Customize(x => x.WaitForNonStaleResults()).First();
var b = session.Query<B>().Customize(x => x.WaitForNonStaleResults()).First();
Console.WriteLine("A: Id = {0}", a.Id);
Console.WriteLine(" BId = {0}", a.BId);
Console.WriteLine();
Console.WriteLine("B: Id = {0}", b.Id);
Console.WriteLine(" Name = {0}", b.Name);
Console.WriteLine();
var guid = a.Id;
var ab = session.Query<AB, MyIndex>().Customize(x => x.WaitForNonStaleResults())
.FirstOrDefault(t => t.AId == guid);
if (ab == null)
Console.WriteLine("AB: NULL");
else
{
Console.WriteLine("AB: AId = {0}", ab.AId);
Console.WriteLine(" BId = {0}", ab.BId);
Console.WriteLine(" BName = {0}", ab.BName);
Console.WriteLine();
}
}
Console.WriteLine();
Console.WriteLine("Done.");
Console.ReadLine();
}
}
class A
{
public Guid Id { get; set; }
public Guid BId { get; set; }
}
class B
{
public Guid Id { get; set; }
public string Name { get; set; }
}
class AB
{
public Guid AId { get; set; }
public Guid BId { get; set; }
public string BName { get; set; }
}
class MyIndex : AbstractMultiMapIndexCreationTask<MyIndex.ReduceResult>
{
public MyIndex()
{
AddMap<A>(docs => from a in docs
select new
{
AId = a.Id.ToString().Split('/')[1],
a.BId,
BName = (string)null
});
AddMap<B>(docs => from b in docs
select new
{
AId = (string)null,
BId = b.Id.ToString().Split('/')[1],
BName = b.Name
});
Reduce = results => from result in results
group result by result.BId
into g
select new
{
g.FirstOrDefault(x => x.AId != null).AId,
BId = g.Key,
g.FirstOrDefault(x => x.BName != null).BName
};
}
internal class ReduceResult
{
public string AId { get; set; }
public string BId { get; set; }
public string BName { get; set; }
}
}
}
You can provide an ID to RavenDB explicitly upon saving:
session.Store(doc, explicitIdValueString);
The explicitIdValueString can be a Guid string. This value will be used to identify the document within the entire database and will not be prefixed by a type tag name. You can also customized the tag name, or the ID generation strategy all together by overriding conventions on IDocumentStore.Conventions such as FindTypeTagName which is a Func<Type, string>.
The main problem is that while RavenDB can deal with numeric / integer on the client, but on the server side, RavenDB uses string ids.
In general, it isn't recommended to use Guids / numeric ids.
Suppose you have Users and you want to generate guid identifiers for these.
new User { Id = "users/" + Guid.NewGuid().ToString("N") }
For sanity purposes, in documents i eagerly create keys for I set them up as immutable.
public class User
{
public User(Guid? guid = null)
{
IdPart = (guid ?? Guid.NewGuid()).ToString("N")
}
string IdPart { get; }
string Id => $"Users/{IdPart}"
Sometimes IdPart actually a whole key. Suppose we have "Users/abc". If a user has a Project. I will commonly create a document similar to:
public class Project
{
public User(Guid? userId = null)
{
UserId = "Users/" + (guid ?? Guid.NewGuid()).ToString("N");
Id = $"{UserId}/project/"
}
Note the trailing project/ this will inform raven to create a HiLo value after the slash.
This concept can be used to easily intermix both assigned identifiers, natural identifiers, and sequence/hilo/identity keys while promoting readable identifiers as opposed to 1. 1 is what? But User/abc/project/1, i can tell you what that is. The first project created by abc
class MyIndex : AbstractIndexCreationTask<AB>
{
public MyIndex()
{
Map = docs =>
from d in docs
select new
{
d.Id,
d.BId,
BName = string.Empty
};
TransformResults = (database, results) =>
from r in results
let b = database.Load<B>("bs/" + r.BId.ToString())
select new
{
Id = Guid.Parse(r.Id.ToString().Split('/').Last()),
r.BId,
BName = b.Name
};
}
}

NHibernate - query specific columns and return distinct records?

I am new to NH.
I have a table in a legacy DB that looks like this:
Id,
CompanyId,
Description,
[LOADS of other columns here]
I would like to return a DISTINCT set of data using NHibernate, selecting only specific columns and using a WHERE statement. The SQL would looks something like this:
SELECT DISTINCT
[table_name].CompanyId,
[table_name].Description
FROM
[table_name]
WHERE
[table_name].CompanyId = 2
Having googled this I came up with:
ProjectionList projections = Projections.ProjectionList();
projections.Add(Projections.Property("CompanyId"), "CompanyId");
projections.Add(Projections.Property("Name"), "SomeName");
var companyDto = session.QueryOver<Company>()
.Where(x => x.CompanyId == 2)
.Select(projections)
.TransformUsing(Transformers.AliasToBean<CompanyDto>())
.List<CompanyDto>();
if (companyDto != null)
Console.WriteLine(string.Format("{0}, {1}", companyDto.CompanyId, companyDto.SomeName));
Where the DTO is:
public class CompanyDto
{
public int CompanyId { get; set; }
public string SomeName { get; set; }
}
And the entity is:
public class Company
{
public virtual int Id { get; private set; }
public virtual int CompanyId { get; set; }
public virtual string Name { get; set; }
}
This does not bring back disinct records. I know that normally I would have to use a different transform (DistinctRootEntity) but I cannot use two transforms. How can I combine all of the things I want, into a single call? It must be possible, its basic SQL ....
I need to:
not use HQL
not bring back all columns for the record
not bring back duplicate rows
there is a Projection for this
var projections = Projections.Distinct(Projections.ProjectionList()
.Add(Projections.Property("CompanyId").As("CompanyId"))
.Add(Projections.Property("Name").As("SomeName"));

Workaround for selectmany in ravendb using client api

I have a ravendb class like such:
public class Student
{
public string Id { get; set; }
public string TopLevelProperty { get; set; }
public Dictionary&ltstring, string&gt Attributes { get; set; }
public Dictionary&ltstring,List&ltDictionary&ltstring, string&gt&gt&gt CategoryAttributes { get; set; }
}
and a document like so:
The following linq will not work due to a selectmany:
test = (from student in session.Query()
from eduhistory in student.CategoryAttributes["EducationHistory"]
where eduhistory["StartYear"] == "2009"
select student).ToList();
How can I get all students where StartYear == 2009?
This does it :
test = session.Advanced.LuceneQuery()
.Where("CategoryAttributes.EducationHistory,StartYear:2009")
.ToList();
Notice the comma rather than a dot after EducationHistory. This indicates that we are looking at the list to find a property in one of the items named StartYear.
If I wanted greater than :
test = session.Advanced.LuceneQuery()
.Where("CategoryAttributes.EducationHistory,StartYear:[2009 TO null]")
.ToList();
etc etc.
This should work:
test = (from student in session.Query()
where student.CategoryAttributes["EducationHistory"].Any(edu => edu["StartYear"]== "2009" )
select student).ToList();