I have an entity framework 3.5 project where I'm using TPH inheritence. Two of my concrete types are in their own DAO class and contain a selector that projects the Entity Class into a DTO. However, both of these concrete classes have similar relations with another table which I use a let statement to identity clearly. My question is, can I somehow refactor this let statement because as I create more inherited concrete classes I feel I'm violating DRY.
StudentDAO.cs:
var myDTO = from x in context.Students
let address = (from a in x.addresses where a.active == true select a).FirstOrDefault()
select new StudentDTO { id = x.id, studentname = x.studentname, address = a.address};
ProfessorDAO.cs:
var myDTO = from x in context.Professors
let address = (from a in x.addresses where a.active == true select a).FirstOrDefault()
select new ProfessorDTO { id = x.id, professorname = x.studentname, address = a.address};
so is there a way I can refactor the address out of both of these queries?
Short answer: I'm afraid there isn't much you could easily do. The problem is that you need to parameterize the query. The query however needs to be represented as an expression tree (so that it can be translated to SQL or whatever EF uses). Unfortunately, C# don't provide any elegant ways for composing expression trees.
Long answer: Some time ago, I wrote a C# article that explains how to compose queries (using some tricks) - you can take expression tree and splice it into a parameterized query. So, in principle you could use the techniques from the article to create a method that takes the changing parts of the expression tree and then compose the query.
I'll use the types used in LINQ to SQL, because I'm more familiar with it, but I believe the principle should be the same:
IQueryable<R> GetInfo<T>(IQueryable<T> source, // e.g. context.Students
Expression<Func<T, IQueryable<Address>>> getAddr, // x => x.adresses
Expression<Func<T, Address, R>> getRes // (x, a) => new StudentDTO { ... }
return
from x in source.AsExpandable()
let address = (from a in getAddr(x).Expand() where a.active == true
select a).FirstOrDefault()
select getRes(x, a).Expand();
}
// The use would look like this
var res = GetInfo(context.Professors, x => x.addresses, (x, a) => new
ProfessorDTO { id = x.id, professorname = x.studentname, address = a.address });
So a summary is that C# and LINQ do not provide any built-in support that would make it easier. However, with some effort you can write query that is parameterized by some parts of the expression tree. In your case, it makes the code a little shorter and less repetitive, but it makes it a lot more complex (and you also need to use library like the one I referenced that provides AsExpandable and Expand).
In this case I'm afraid the it's not worth the effort. But it would be nice if C# v(Next)^x provided some more elegant support for things like this :-)
Related
I'm trying to use lambda to join 5 tables. What I would like to know is if I'm doing it right and if there's a better way of doing it. Any suggestion is much appreciated.
var myList = _context.Event
.Join(_context.EventHost,
e => e.EventHostId,
eh => eh.Id,
(e, eh) => new { Event = e, EventHost = eh })
.Join(_context.EventAddress,
eh => eh.EventHost.EventAddressID,
ea => ea.ID,
(eh, ea) => new { EventAddress = ea, EventHost = eh })
.Join(_context.OrderHeaderGroup,
ea => ea.EventHost.Event.Id,
ohg => ohg.EventId,
(ea, ohg) => new { EventAddress = ea, OrderHeaderGroup = ohg })
.Join(_context.OrderHeader,
ohg => ohg.OrderHeaderGroup.OrderHeaderGroupID,
oh => oh.OrderHeaderGroupID,
(oh, ohg) => new { OrderHeader = oh, OrderHeaderGroup = ohg })
.Select(c => new {
c.OrderHeader.OrderHeaderGroup.EventId,
c.OrderHeader.EventAddress.EventAddress.Name,
c.OrderHeader.EventAddress.EventAddress.Phone,
c.OrderHeader.EventAddress.EventAddress.Address1,
c.OrderHeader.EventAddress.EventAddress.Address2,
c.OrderHeader.EventAddress.EventAddress.City,
c.OrderHeader.EventAddress.EventAddress.Country,
c.OrderHeader.EventAddress.EventAddress.PostalCode,
c.OrderHeader.EventAddress.EventAddress.Email,
c.OrderHeader.EventAddress.EventHost.Event.ConsultantId,
c.OrderHeader.EventAddress.EventHost.Event.StartDate,
c.OrderHeader.EventAddress.EventHost.Event.EndDate,
}).Where(x => x.ContactId == contactId && x.StartDate >= newStartDate && x.EndDate <= newEndDate).ToList();
Yes, use the query syntax, as is the recommendation from Microsoft: Check here
At compile time, query expressions are converted to Standard Query Operator method calls according to the rules set forth in the C# specification. Any query that can be expressed by using query syntax can also be expressed by using method syntax. However, in most cases query syntax is more readable and concise.
from event in _context.Event
join eventHost in _context.EventHost
on event.EventHostId equals eventHost.Id
join eventAddress in _context.EventAddress
on eventHost.EventAddressId equals eventAddress.Id
// etc
select new {/*You new anonymous type here*/}
The issue here isn't preferential code readability. Which, BTW, I think Lambda is far more readable than Query form. But that's because I'm a programmer.
The issue is that you are trying to take a SQL Developer's perspective and SHOVE It into programming. I did that when LinqToEntities was first a thing, where I start in SSMS et al, I write a query, then I do Transliteration into C#.
The solution is to use your ORM. If you use a feature rich ORM, you should have something called Navigation Properties. These already have implicit joins for you that are either implicitly, or explicitly declared in your Fluent mapping. So you shouldn't have to re-describe every join every time you write a LINQ statement.
If you trust the language and configure your system correctly, you can end up with something like this:
// This is not shortened, this should be the whole thing.
var myList = _context.OrderHeader
.Where(x => x.EventAddress.Something.ContactId == contactId
&& x.EventAddress.Something.StartDate >= newStartDate
&& x.EventAddress.Something.EndDate <= newEndDate)
.Select(c => new DetailEntityView(c)).ToList();
I also suggest using "View" classes. These aren't entities that are configured to persist to the DB, but are like DB Views, in C#. They are less used classes that just transfer data.
public class DetailEntityView
{
public int EventId { get; set; }
public string Name { get; set; }
// ... etc
public DetailEntityView(OrderHeader c)
{
EventId = c.OrderHeaderGroup.EventId;
Name = c.EventAddress.EventAddress.Name;
// ... etc
}
}
Now, some ORMs are less featured than the EF's and NH's of the world. So in that case, I'm sorry you or your team chose something else.
Also, all of your joins are on IDs, which is as it should be. But SOME Custom queries join on field that are not constrained, like name fields or other values. In those cases, you do need to add those specific joins to your query. But 1.) Ew. And 2.) Why? And 3.) Then you can do what you did in your OP.
I am new to c#, and am using Linq To SQL in Linqpad to fetch data from the database. In my database I have many columns which for legacy reasons, are names Col1, Col2, Col3 etc.
At the moment I have to remember which Column references which value, to enable me to extract data.
Instead of having to write:
Clients.Where(c => c.Col1443 == true)
I would like to be able to do something like the following:
var HasRedEyes= "Col1443";
Clients.Where(c => c.HasRedEyes == true)
Please note that I need this to translate to SQL, as I need to fetch this data from the database, this data is not localised.
You really should find a way to A. Rename your columns and B. Reduce the number of them (1,000+ is a bit excessive). It's been a while since I used LINQ to SQL but if all you need is querying abilities, could you define a view with the columns aliased instead?
That aside, one way to solve this would be to define named expressions that represent predefined filters. For example, assuming the class is named Client:
Expression<Func<Client, bool>> hasRedEyes = c => c.Col1443; // == true is implicit
var query = Clients.Where(hasRedRyes).ToList();
But note those are only composable if you AND them via successive calls* to Where:
Expression<Func<Client, bool>> hasRedEyes = c => c.Col1443;
Expression<Func<Client, bool>> hasBrownHair = c => c.Col1567 == "brown";
var query = Clients.Where(hasRedEyes).Where(hasBrownHair).ToList();
If you need an OR you'd have to define the or'd filter as its own predefined expression*, for example:
Expression<Func<Client, bool>> hasRedEyesOrBrownHair = c => c.Col1443 || c.Col1567 == "brown";
var query = Clients.Where(hasRedEyesOrBrownHair).ToList();
(* The alternative to the last bit would be to either use a tool like LINQKit or to manually use the Expression API. With the latter you'd have to take the two seperate conditions, retrieve the body of the individual lambdas expressions, use an expression visitor to replace the parameter of one of them to match the other, call Expression.Or and finally Expression.Lambda to create a new filter you could pass to Where. This same concept can be applied to and'ing as well, though it all gets a bit tricky.)
I'm having issues trying to reuse ProjectionLists in NHibernate QueryOvers. I can't work out how to reuse things for different root entities.
Object model is roughly represented as:
Breakfast one to many Pastry many to zero-or-one Coffee
The two separate queries are roughly:
session.QueryOver<Breakfast>()
.Where(b => b.Id == searchId)
.Inner.JoinQueryOver(b => b.Pastries, () => pastry)
.Left.JoinAlias(p => p.Coffee, () => coffee)
.Select(projections)
.TransformUsing(Transformers.AliasToBean<TargetDto>())
.List<TargetDto>();
session.QueryOver<Coffee>()
.Where(c => c.Id == searchId)
.Inner.JoinQueryOver(c => c.Pastries, () => pastry)
.Select(projections)
.TransformUsing(Transformers.AliasToBean<TargetDto>())
.List<TargetDto>();
The common projections I'm trying to reuse looks like this:
var projections = Projections.ProjectionList()
.Add(Projections.Property(() => pastry.Name, () => target.PastryName))
.Add(Projections.Property(() => coffee.Name, () => target.CoffeeName));
These projections, using the aliases, work fine for the first query (root: Breakfast), because they're not trying to pull off properties that are on that root entity. On the second query (root: Coffee), it explodes saying it can't find 'coffee.Name' on Coffee, because it doesn't like the alias. The QueryOver(() => coffee) syntax doesn't help because it doesn't actually register 'coffee' as an alias, it just uses it for type inference. Oh bloody hell that was the problem. There's a stupid piece of application infrastructure that is breaking the alias syntax to not actually use the alias version underneath.
The second query wants the projections to look like:
var projections = Projections.ProjectionList()
.Add(Projections.Property(() => pastry.Name, () => target.PastryName))
.Add(Projections.Property<Coffee>(c => c.Name, () => target.CoffeeName));
However this is now incompatible with the first query.
Is there any way of making this work, so the I can project properties without knowing what the root entity type is?
I think all you need to do is assign the coffee alias in the session.QueryOver<Coffee> call:
Coffee coffee = null;
session.QueryOver<Coffee>(() => coffee)
/* etc */
The below might be completely unrelated to what you're doing, but I figured I'd include it in case anyone else is writing code that passes around QueryOver aliases.
I'd add a word of caution-- reusing aliases across different queries like this can be a little dangerous.
NHibernate takes the expression () => coffee and grabs the name of the alias you're using from the expression (in this case, "coffee") and then uses it in the generated SQL as an alias. This means that depending on how your code is structured, shared projections like this could break if alias names change.
For example, say you had the following method to return some shared projections:
public ProjectionList GetSharedProjections()
{
Coffee coffee = null;
TargetDTO target;
var projections = Projections.ProjectionList()
.Add(Projections.Property(() => coffee.CoffeeName)
.WithAlias(() => target.CoffeeName));
return projections;
}
Then you had some code calling your helper method:
session.QueryOver<Coffee>(() => coffee)
.Select(GetSharedProjections());
Everything will work fine-- as long as your aliases match. The second anyone changes either of the aliases though, the query will fail.
You might be tempted to pass in an alias to a method like this:
public ProjectionList GetSharedProjections(Coffee coffeeAlias)
{
/* Same as above except with "coffeeAlias"
}
And then pass in your alias:
session.QueryOver<Coffee>(() => coffee)
.Select(GetSharedProjections(coffee));
But this won't work either. Remember that NHibernate is grabbing the name of the alias and using it directly in the generated SQL. The above code will try to use both "coffee" and "coffeeAlias" in the generated SQL and will fail.
One way to properly do this (without just hoping nobody changes alias names) is to pass around expressions and use those to reconstruct property names with the correct aliases.
You'd create a helper method that builds property access using an alias and a property:
public static PropertyProjection BuildProjection<T>(
Expression<Func<object>> aliasExpression,
Expression<Func<T, object>> propertyExpression)
{
string alias = ExpressionProcessor.FindMemberExpression(aliasExpression.Body);
string property = ExpressionProcessor.FindMemberExpression(propertyExpression.Body);
return Projections.Property(string.Format("{0}.{1}", alias, property));
}
Then, you could change the GetSharedProjections method to take an alias in the form of an expression:
public ProjectionList GetSharedProjection(Expression<Func<Coffee>> coffeeAlias)
{
TargetDTO target = null;
var projections = Projections.ProjectionList()
.Add(BuildProjection<Coffee>(coffeeAlias, c => c.CoffeeName))
.WithAlias(() => target.CoffeeName);
}
Now, calling your method would look like this:
session.QueryOver<Coffee>(() => coffee)
.Select(GetSharedProjections(() => coffee));
When someone changes your alias name you're covered. You can also safely use this method among many queries without worrying about what the name of the alias variable actually is.
Disclaimer: The following is a link to my personal blog
You can find more information about building QueryOver queries this way here.
I am trying to get the columns dynamically. In NHibernate i can do this:
var list = _session.CreateCriteria(typeof(Person))
.SetProjection(Projections.ProjectionList()
.Add(Projections.Property("FirstName"))
.Add(Projections.Property("LastName"))
.Add(Projections.Property("Jersey"))
.Add(Projections.Property("FortyYard"))
.Add(Projections.Property("BenchReps"))
.Add(Projections.Property("VertJump"))
.Add(Projections.Property("ProShuttle"))
.Add(Projections.Property("LongJump"))
.Add(Projections.Property("PersonSchoolCollection"))
)
.SetResultTransformer(new NHibernate.Transform.AliasToBeanResultTransformer(typeof(Person)))
.List<Person>();
What is the equivalent in Linq?
As you also tag linq-to-sql and linq-to-entities I assume you are looking for an equivalent in Linq-to-Sql or Entity Framework. The two answers (so far) would be such equivalents if _session.Query<Person>() were replaced by context.Persons. (Although Darius's answer would throw an exception saying that you can't create entity instances in an entity query).
But apart from the possibility to use Select to create an ad hoc projection, one of the newer features of AutoMapper makes it even easier. Automapper is a very popular tool to map (say: project) a list of types to another list of types. But until recently its drawback was that it only worked on in-memory lists, i.e. the projection did not propagate into the SQL query. So it could not be used to cut back the number of queried fields (as NHibernate projections does).
This problem was described in Stop using AutoMapper in your Data Access Code (the title says it all). But it also offered a preliminary but excellent fix, which was later adopted by Automapper itself.
This feature makes it possible to write very succinct code like:
var dtoList = context.Persons.Project().To<PersonDto>();
(after the mapping between Person and PersonDto was registered in Automapper).
Now the SQL query only contains the fields that are used for PersonDto.
Isn't that would work:
_session.Query<Person>()
.Select(new {FirstName, LastName, Jersey, FortyYard})
.ToList()
.Select(x => new Person() {
FirstName = x.FirstName,
LastName = x.LastName,
Jersey = x.Jersey,
FortyYard = x.FortyYard
}
);
var list = from person in context.Persons
select new Person()
{
FirstName = person.FirstName,
LastName = person.LastName,
Jersey = person.Jersey,
FortyYard = person.FortyYard,
BenchReps = person.BenchReps,
VertJump = person.VertJump,
ProShuttle = person.ProShuttle,
LongJump = person.LongJump,
PersonSchoolCollection = person.PersonSchoolCollection
};
Why cant I do this:
usuariosEntities usersDB = new usuariosEntities();
foreach (DataGridViewRow user in dgvUsuarios.Rows)
{
var rowtoupdate =
usersDB.usuarios.Where(
u => u.codigo_usuario == Convert.ToInt32(user.Cells[0].Value)
).First();
rowtoupdate.password = user.Cells[3].Value.ToString();
}
usersDB.SaveChanges();
And have to do this:
usuariosEntities usersDB = new usuariosEntities();
foreach (DataGridViewRow user in dgvUsuarios.Rows)
{
int usercode = Convert.ToInt32(user.Cells[0].Value);
var rowtoupdate =
usersDB.usuarios.Where(u => u.codigo_usuario == usercode).First();
rowtoupdate.password = user.Cells[3].Value.ToString();
}
usersDB.SaveChanges();
I must admit it is a more readable code but why cant this be done?
The thing about it is that LINQ queries are transformed by the compiler into an expression tree. This expression tree is then converted into T-SQL and passed to the server. LINQ to SQL maps certain methods like String.Contains to the T-SQL equivalents.
In the first example, LINQ apparently does not map Convert.ToInt32 to anything and the exception is thrown. The reason it works in your second example is because the Convert.ToInt32 call is done outside of the query so it isn't part of the expression tree and doesn't need to be converted to T-SQL.
This MSDN page describes how LINQ to SQL translates various data types, operators, and methods into T-SQL. (Although the documentation does suggest Convert.ToInt32 is supported so I'm not sure what else might be going on here.)
Sorry just realized this is ADO.NET Entity Framework, not LINQ to SQL. This page lists the ADO.NET Entity Framework mappings. It is a bit more restrictive, mostly because it needs to work with multiple providers.
Because your LINQ to Ent. doesn't compile the query into MSIL with all the meta data, but simply translates the query into a few extantion methods and is bounded to lambda parsing abuilities of the languge. That means that
this code:
var results = from c in SomeCollection
where c.SomeProperty < someValue * 2
select new {c.SomeProperty, c.OtherProperty};
is the same as this:
var results =
SomeCollection
.Where(c => c.SomeProperty < someValue * 2)
.Select(c => new {c.SomeProperty, c.OtherProperty});