Concatenating two queries bases on a foreign key - c#

I'm (at least trying) to implement Repository pattern in my .NET C# project so when I need to communicate with the database I us something like this:
IList<Sole> soles = SoleService.All().ToList();
As the name of the method called from the service suggest with the query above I get all records form Sole table. I don't want and I think this is the right way to implement this pattern, to keep too much custom logic in my service. What I mean is that I only want to keep the All() method and each modification of the result to be made outside the service methods.
The current problem is this. I have entity Sole and entity SoleColor. SoleColor has a foreign key column SoleID making the relation between the two tables. Right now for those two entities I can call only All() method :
var soleColors = SoleColorService.All();
var soles = SoleService.All();
But here I need some customization in the form of selecting only those rows from Sole that are related with the SoleColor entity. In other words only end up with a list of only those rows from Sole where Sole.ID can be found as a foreign key in SoleColor SoleID foreign key.
Right now I'm a bit confused - it's been a while since I last used plain SQL synthax. I think this is easily achieved using SQL and JOIN. But when LINQ is involved and my experience so far tells me that I need those two queries :
var soleColors = SoleColorService.All();
var soles = SoleService.All();
And then make some kind of JOIN/UNION to filter only the results I need.
So which tools I need to use in this kind of situation cause it's not the only place I'm gonna need this and I want to learn to do it myself and of course to do it in this current situation?

After your last comment I think this is what you're looking for:
from s in SoleService.All()
join sc in SoleColorService.All() on s.ID equals sc.SoleID
select s
But this only works if both repositories have the same context instance. If not, you have to do it in two steps:
var ids = SoleColorService.All().Select(sc => sc.SoleID).ToArray();
var soles = SoleService.All().Where(s => ids.Contains(s.ID));
I'm a bit suspicious though about the static All() methods. They suggest that you use static contexts, which is considered bad practice. Further I wonder about the associations. By the sound of the words I'd expect Sole to have a SoleColor, i.e. Sole to have a SoleColorId FK.

Related

Entity Framework with multiple databases

Is there anyway to map multiple SQL Server databases in a single EF context? For instance I'm trying to do something like this
select order from context.Orders
where context.Users.Any(user => user.UserID == order.UserID)
And I'd like to get generated SQL along the lines of:
select .. from store.dbo.order where userID in
(select userID from authentication.dbo.user)
and note that the database names are different - store in one place, authentication in the other.
I've found a few articles that deal with multiple schema ('dbo' in this case), but none dealing with multiple database names.
As a potential workaround, you could create a view of the table from the second database in the first database and point your mappings to the view.
I'm pretty sure this isn't possible. The context derives from DbContext.
A DbContext instance represents a combination of the Unit Of Work and Repository patterns such that it can be used to query from a database and group together changes that will then be written back to the store as a unit. DbContext is conceptually similar to ObjectContext.
Configuration (connection string, schema, etc) for a DbContext is specific to a single database.
It's not possible. A notion of context is below notion of a database, and allowing this would probably be a bad practice. Allowing such a thing could cause developers to forget that they are dealing with two databases, and to take care about all performance implications that come from that.
I imagine you should still be able use two contexts and write elegant code.
var userIds = AuthContext.Users
.Where(user => user.Name = "Bob")
.Select(user => user.UserId)
.ToList();
var orders = StoreContext.Orders
.Where(order => userIds.Contains(order.UserId))
.ToList()
First execute query on authentication database context, in order to provide parameters for second query.

Is there a quick way to get every association between two entities?

I have two tables in my database: TPM_AREAS and TPM_WORKGROUPS. There exists a many-to-many relationship between these two tables, and these relationships are stored in a table called TPM_AREAWORKGROUPS. This table looks like this:
What I need to do is load all these mappings into memory at once, in the quickest way possible. As TPM_AREAWORKGROUPS is an association, I can't just say:
var foo = (from aw in context.TPM_AREAWORKGROUPS select aw);
I can think of three ways to possibly do this, however I'm not quite sure how to accomplish each of them nor which one is the best.
1) Load in every workgroup, including the associated areas:
Something like:
var allWG = (from w in context.TPM_WORKGROUPS.Include("TPM_AREAS")
where w.TPM_AREAS.Count > 0
select w);
// Loop through this enumeration and manually build a mapping of distinct AREAID/WORKGROUPID combinations.
Pros: This is probably the standard EntityFramework way of doing things, and doesn't require me to change any of the database structure or mappings.
Cons: Could potentially be slow, since the TPM_WORKGROUPS table is rather large and the TPM_AREAWORKGROUPS table only has 13 rows. Plus, there's no TPM_AREAWORKGROUPS class, so I'd have to return a collection of Tuples or make a new class for this.
2) Change my model
Ideally, I'd like a TPM_AREAWORKGROUP class, and a context.TPM_AREAWORKGROUP property. I used the designer to create this model directly from the database, so I'm not quite sure how to force this association to be an actual model. Is there an easy way to do this?
Pros: It would allow me to select directly against this table, done in one line of code. Yay!
Cons: Forces me to change my model, but is this a bad thing?
3) Screw it, use raw SQL to get what I want.
I can get the StoreConnection property of the context, and call CreateCommand() directly. I can then just do:
using (DbCommand cmd = conn.CreateCommand())
{
cmd.CommandText = "SELECT AreaId, WorkgroupId FROM TPM_AREAWORKGROUPS";
var reader = cmd.ExecuteReader();
// Loop through and get each mapping
}
Pros: Fast, easy, doesn't require me to change my model.
Cons: Seems kind of hacky. Everywhere else in the project, we're just using standard Entity Framework code so this deviates from the norm. Also, it has the same issues as the first option; there's still no TPM_AREAWORKGROUPS class.
Question: What's the best solution for this problem?
Ideally, I'd like to do #2 however I'm not quite sure how to adjust my model. Or, perhaps someone knows of a better way than my three options.
You could do:
var result = context
.TPM_WORKGROUPS
.SelectMany(z => z.TPM_AREAS.Select(z2 => new
{
z2.AREAID,
z.WORKGROUPID
}));
The translated SQL will be a simple SELECT AREAID, WORKGROUPID FROM TPM_AREAWORKGROUPS.
About other options:
I wouldn't use option 3) because I personnally avoid raw SQL as much as possible when using Entity Framework (see https://stackoverflow.com/a/8880157/870604 for some reasons).
I wouldn't use option 2) because you would have to change your model, and there is a simple and efficient way that allows to not change it.
What about use projection to load data?
You could do that do fill a annonymous object and then work with it the way you like.

Modeling an aggregate result set in a (mini) ORM

I'm using the PetaPoco mini-ORM, which in my implementation runs stored procedures and maps them to object models I've defined. This works very intuitively for queries that pull out singular tables (i.e. SELECT * FROM Orders), but less so when I start writing queries that pull aggregate results. For example, say I've got a Customers table and Orders table, where the Orders table contains a foreign key reference to a CustomerID. I want to retrieve a list of all orders, but in the view of my application, display the Customer name as well as all the other order fields, i.e.
SELECT
Customers.Name,
Orders.*
FROM
Orders
INNER JOIN Customers
ON Orders.CustomerID = Customers.ID
Having not worked with an ORM of any sort before, I'm unsure of the proper method to handle this sort of data. I see two options right now:
Create a new aggregate model for the specific operation. I feel like I would end up with a ton of models in any large application by doing this, but it would let me map a query result directly to an object.
Have two separate queries, one that retrieves Orders, another that retrieves Customers, then join them via LINQ. This seems a better alternative than #1, but similarly seems obtuse as I am pulling out 30 columns when I desire one (although my particular mini-ORM allows me to pull out just one row and bind it to a model).
Is there a preferred method of doing this, either of the two I mentioned, or a better way I haven't thought of?
Option #1 is common in CQRS-based architectures. It makes sense when you think about it: even though it requires some effort, it maps intuitively to what you are doing, and it doesn't impact other pieces of your solution. So if you have to change it, you can do so without breaking anything elsewhere.

Querying from the Controller a List<T> obtained fromt the repository increase coupling?

I have an ASP.NET MVC application coded with C#. The application is structured this way:
Controller
Repository
LINQ to Entities (Entity Framework)
View
I use the Repository (_ProductRep) to query the LINQ to Entities and give to the Controller actual entities or List<T>, not IQueriables<T>.
I would like to have some help about a situation where I have more than a doubt. I have the following code:
List<Monthly_Report> lproduct_monthlyReport = _ProductRep.GetArchiveReport(product.Prod_ID, lmonth, lyear);
After I get this lproduct_monthlyReport I need to query it inside a foreach and get a specific record. Currently I implemented the solution like this:
foreach (var item in litemList)
{
var lproductItem_monthlyReport = lproduct_monthlyReport.Single(m => m.Item_ID == item.Item_ID);
// Other code
}
Where litemList is the list of all the possible items a product can have.
I wanted to know whether this solution sensibly increase the coupling (and violates the law of Demeter) or it is acceptable because I am actually querying a List<T> and not an IQueriable<T>. Correct me if I am wrong, but I guess that since the List does not need to access the EF DataContext, there is no coupling between Controller and EF.
In case I am wrong, the only solution I can think about is to substitute the query with a Repository method (that still I have to implement):
var lproductItem_monthlyReport_ProductRep.GetArchiveReport(product.Prod_ID, lmonth, lyear, item.Item_ID);
with this solution however the Repository makes one query with 4 conditions every loop cycle whilst in the previous solution the repository was making a query with just one conditions.
May you please enlighten me on this issue? Thanks.
PS: I need both variables lproduct_monthlyReport and lproductItem_monthlyReport inside the loop, I cannot just use one of them
PPS: I know that I should have a Business Service Layer between Controller and Repository, it is my next step.
Returning Lists from your repository will give you awful performance, because you lose the deferred execution behaviour. Basically your repository will retrieve every single record, and not related entities, into memory, and turn them into a List, which then gets processed in memory. If you want to access a related entity, it'll need another database hit. If you stick with IEnumerable (or IQueryable), then you are hiding the nuances of the entity framework behaviour from the client, but still getting the advantages like lazy loading and deferred execution.
Ignoring the specifics of your Repository for now, if you do this:
List<Product> products = MyEntities.Products.ToList();
Product product1 = products.Single(p => p.Id = 1);
it will perform much worse than this:
IEnumerable<Product> products = MyEntities.Products;
Product product1 = products.Single(p => p.Id = 1);
The first one will perform a SELECT in the database with no WHERE clauses, then instantiate .Net objects for every result, then query that in-memory list. The second will do nothing until you access a property on product1 and will at that point issue a database command to just retrieve the 1 product, and only instantiate that 1 product.
The difference between the 2 may not be noticeable with small data sets, but as the data set gets larger this will get worse and worse. Throw in a connected entity (or worse still entity collection), and you'll get potentially thousands of database hits, where if you stuck with IEnumerable you'd get 1.
I would probably have function like this GetArchiveReport(int prodID, int lmonth, int lyear, IEnumerable<int> itemIDs) that would do a itemIDs.Contains(tbl.ID) inside your query
var SelectedReports = _ProductRep.GetArchiveReport(product.Prod_ID, lmonth, lyear, litemList.Select(item => item.Item_ID));
foreach(var prodItem in SelectedReports)
{
//Do code
}

Linq: To join or not to join (which is the better way, joins or relationships)

I have written quite a bit of code which uses the Linq2Sql table relationships provided to me just by having foreign keys on my database. But, this is proving to be a bit laborious to mock data for my unit tests. I have to manually set up any relationships in my test harness.
So, I am wondering if writing Linq joins rather than relying on the relationships would give me more easily testable and possibly more performant code.
var query =
from orderItem in data.OrderItems
select new
{
orderItem.Order.Reference,
orderItem.SKU,
orderItem.Quantity,
};
Console.WriteLine("Relationship Method");
query.ToList().ForEach(x => Console.WriteLine(string.Format("Reference = {0}, {1} x {2}", x.Reference, x.Quantity, x.SKU)));
var query2 =
from orderItem in data.OrderItems
join order in data.Orders
on orderItem.OrderID equals order.OrderID
select new
{
order.Reference,
orderItem.SKU,
orderItem.Quantity,
};
Console.WriteLine();
Console.WriteLine("Join Method");
query2.ToList().ForEach(x => Console.WriteLine(string.Format("Reference = {0}, {1} x {2}", x.Reference, x.Quantity, x.SKU)));
Both queries above give me the same result, but is one better than the other in terms of performance and in terms of testability?
What are you testing? Linq to SQL's ability to read data? It is generally assumed that, linq to sql being a thin veneer over a database, that the linq to sql code itself is considered "pristine," and therefore doesn't need to be tested.
I am hugely not in favor of complicating your code in this way, just so that you can mock out the linq to sql DBML. If you want to test your business logic, it is far better to just hook up a test database to the DBML (there is a constructor overload for the datacontext that allows you to do this) and use database transactions to test your data interactions. That way, you can roll the transaction back to undo the changes to the database, leaving the test database in its original state.
In terms of performance, both queries will evaluate to the same SQL (Scott Guthrie has a blog post on how to view the SQL generated by LINQ queries). I don't think that either option is inherently more "testable" than the other. However, I prefer to use the foreign keys and relationships because when using SQL Metal it lets you know really quickly that your database has the appropriate keys.
I don't think either approach has an advantage in either performance or testability. The first form is easier to read though, and so I would personally go with that. It's a subjective matter though.
It seems to me that your problem lies with being able to setup your data in an easy way, and have the foreign key values and entity references remain consistent. I don't think that's an easy thing to solve. You could write some sort of framework which creates object proxies and uses the entity metadata to intercept FK and related entity property setters in order to sync them up, but before you know it, you'll have implemented an in-memory database!

Categories