Strongly typed access to result-set from Linq Join - to display in MVC view - asp.net

I am new to the MVC/Linq party (coming from Java).
I know that I can use something like the following to get strongly typed access, but what about if I want to use a join in my linq query?
public void IQueryable<Product> GetItems(int CategoryID)
{
...linq query...
}enter code here

In your example, if you have used the designer to create your Linq to SQL data context, and there is a foreign key relationship between the Product and Category tables in your database, then the Category will be a property in your Product class, so there is no need to join them.
Something along the lines of the following should help (guessing property names; db is your data context):
public IQueryable<Product> GetItems(int categoryID)
{
return db.Products.Where(p => p.Category.CategoryID == categoryID).AsQueryable();
}
Or if you are not comfortable with the lambda syntax, then the following is synonymous:
public IQueryable<Product> GetItems(int categoryID)
{
var result = from p in db.Products
where p.Category.CategoryID == categoryId
select p;
return result.AsQueryable();
}
The Product class has a Category property, providing strongly typed access to all of the properties in category. You would use something like product.Category.Name to get the category name for example.
Also, there are a plethora of good Linq to SQL tutorials out there. Try the following:
http://it-box.blogturk.net/2007/10/19/linq-to-sql-tutorial-series-by-scott-guthrie-pdf-book-format/
http://www.hookedonlinq.com/LINQtoSQL5MinuteOverview.ashx

Related

Linq to entity enum not comparing

Here is my Enum:
public enum AdvertStatus
{
Active,
Archived
}
And my entity type:
public record Advertisement
{
...
public AdvertStatus Status { get; set; }
...
}
In database it's stored as int, Database is Postgree
When I try to compare it like so:
data = data.Where(x => x.Status == searchValues.Status);
Entity Framework throws an exception sayng:
.Status == (int)__searchValues_Status_3)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'.
I tried solutions from this question: LINQ TO ENTITY cannot compare to enumeration types but it did't work.
EDIT 1:
data is database table context IQueryable<AdvertisementDTO>
searchValues.Status is type of AdvertStatus from search filter
The issue may be higher up in your Linq query, such as you are attempting to project with a Select or ProjectTo before filtering. For simple types like int/string this should work, but depending on how your DTO is declared you might be introducing problems for mpgsql.
For instance if your query is something like:
var query = _context.Advertisements
.Select(x => new AdvertisementDTO
{
// populate DTO
}).Where(x => x.Status == searchValues.Status)
// ....
then npgsql may be having issues attempting to resolve the types between what is in the DTO and the enumeration in your searchValues. From what the exception detail looks like, npgsql is trying to be "safe" with the enum and casting to intbut feeding that to PostgreSQL that results in invalid SQL. I did some quick checks and the DTO would need to be using the same Enum type (C# complains if the DTO cast the value to int, cannot use == between AdvertStatus and int fortunately) The project may have something like a value converter or other hook trying to translate enumerations which is getting brought into the mix and gunking up the works.
Try performing the Where conditions prior to projection:
var query = _context.Advertisements
.Where(x => x.Status == searchValues.Status)
.Select(x => new AdvertisementDTO
{
// populate DTO
})
// ....
If the data value is stored as an Int then this should work out of the box. npgsql does support mapping to string (which would require a ValueConverter) as well as database declared enumerations. (https://www.postgresql.org/docs/current/datatype-enum.html) However, Int columns should work fine /w enums.
If that doesn't work, I'd try with a new DbContext instance pointed at the DB and a simple entity with that Enum to load a row from that table to eliminate whether npgsql is translating the enum correctly, just to eliminate any possible converters or other code that the main DbContext/models/DTOs may be contributing.
It was all my mistake in higher repo Select projection.
Thanks you all for help. Cheers.

Implementing custom paging logic in BLL (not in stored procedures) for ObjectDataSource

So I've implemented objectDataSource custom paging in the DAL using stored proceedures like all the guides and tutorials describe, but I'm realizing that by putting your custom paging logic at the data access layer, you are limited on how much you can use your Domain model.
For instance, I have a webform with a gridview that I want to display every Customer (CustomerId) that's belongs to a specific Office (OfficeId) and the status of their Order (OrderId) if they have one.
So in my services layer I could fill a DTO list like (I'm a newbie when it comes to DDD so I'm sure this design isn't great but bear with me):
ObjectDataSource SelectMethod tied to:
public List<CustomerAndOrderDTO> GetCustomersByOffice(int officeId, int startPageIndex, int maxiumRows)
{
List<CustomerAndOrderDTO> customerAndOrderDTO = null;
List<Customer> customers = Customer.GetCustomersByOffice(int officeId);
foreach (Customer customer in customers)
{
customerAndOrderDTO.Add(new CustomerAndOrderDTO(customer));
}
return customerAndOrderDTO;
}
And this is the DTO class:
public CustomerAndOrderDTO
{
public int OrderId {get; set;}
public int CustomerId {get; set;}
public CustomerAndOrderDTO(Customer customer)
{
CustomerId = customer.Id;
Order order = customer.GetOrder();
OrderId = order.Id;
}
}
Pretty simple code and you get all the flexibility and power of the domain model plus validation and persistence checking while staying in the BLL.
Even if you wanted to ignore the benefits of aggregating your objects in the domain and just combine the data at the stored procedure level like select * from Customers left join Orders on .... left join Office on ... where.... you'd end up writing a million extension methods that implement paging for every possible combination like CustomersOrdersByOfficePaged, CustomersOrdersByAccountDatePaged, etc...
So am I just doing it wrong to begin with? Or does paging at the Service layer make sense? If so, how does one implement it?
So, I'm sure, big picture-wise, this isn't the best architecture, but to implement paging at the service level you can just query your objects from the data access level and then implement the paging logic on your object list at your BLL level like so:
if ((startRowIndex != 0 && startRowIndex != null)
&& (maximumRows != 0 && maximumRows != null))
{
if ((audits.Count - startRowIndex) < maximumRows)
{
audits = audits.GetRange((int)startRowIndex, audits.Count - (int)startRowIndex);
}
else
{
audits = audits.GetRange((int)startRowIndex, (int)maximumRows);
}
}
return audits;
The Count method required by the objectDataSource takes the same parameters as your main "Get" method and calls that "Get" method and then returns the .Count.
Again, not the best way but gets the job done.
Have a look at Dynamic LINQ.
This should help you out:
ScottGu's Blog - Dynamic LINQ
CodeProject's Dynamic Querying with LINQ-to-Entities and
Expressions
Stack Overflow question - Dynamic LINQ OrderBy
Joe Stevens' LINQ to SQL tutorial objectDataSource binding with paging
and sorting
I hope this helps!

ServiceStack ORMLite - Select columns

I recently started working with ServiceStack and its ORMLite framework. I have searched on Google and browsed the source code but couldn't find anything relevent.
Is there any way to select specific columns when executing a query with ORMLite ?
Something like that : Db.First<Model>(q => q.Id == someId, "Column1, Column2")
Unless I missed this feature, I am surprised nobody asked about this before, since this is one the rule of thumbs to optimize your DB transactions.
If you want to specify columns other that the table you need to use SQL as seen in this earlier example
So in your case you could do something like:
Db.First<Model>("SELECT Column1, Column2 FROM AnyTableOrView");
You can also create a partial model that looks at your table by decorating it with the [Alias] attribute, like:
[Alias("AnyTableOrView")]
public class Model {
public int Id { get; set; }
public string Column1 { get; set; }
public string Column2 { get; set; }
}
Then you can do something like:
Db.First<Model>(q => q.Id == someId);
And it will only SELECT + populate fields from the partial model.
I did try this :
Created a Database VIEW (table name and columns are already set)
Created a class named "Event" and matching each fields for that table with a property
(i used [Alias] for table name and for all columns to have nice names)
Wrote access to DB to select 1 record based on it's ID
var dbFactory = new OrmLiteConnectionFactory(
"Data Source=MyDB;User Id=user;Password=pwd", // Connection String
OracleDialect.Provider);
using (var db = dbFactory.OpenDbConnection())
{
var event = db.GetByIdOrDefault<Event>( request.Id );
}
At that point the var 'event' is populated but only the Id field is filled !
all the others fields of the class are not filled (while there are really data in database).
It's the simplest i can do and it does not work. Any ideas ?
(PS : i am using OrmLite for Oracle)
Thanks
I have found the problem.
It was due to an incorrect type matching between field in my class (defined as a string) and the corresponding Oracle Field (that is a DATE).
I replaced the string with datetime and worked like a charm.
So it's working perfectly with a VIEW and that's GREATLY simplify the code.
I had a similar problem, however my solution was different.
I had a int property in my POCO. My query (from Oracle) was returning a null for this property. It caused a exception to be raised and prevented further processing of that row.
The result was a partial populated POCO.
The solution was to change to type to be nullable.
public int? mypropperty

Randomize entity framework query results

Good afternoon,
I have a listview filled using linqdatasource + entity framework iqueryable query.
The query uses a take (top on t-sql) like this:
context.Categories().OrderBy(c=>c.Name).Take(20);
So it brings me the 20 records I want ordered by name.
Now I want to show those 20 records on a random order. Whats the best approach acomplish this?
I believe the answer in this post is what you need:
Linq to Entities, random order
EDIT:
Get your top 20 records first. Then with the top 20 items you've already fetched, randomize them all in C#, not involving the database at all:
var yourRecords = context.Categories().OrderBy(c=>c.Name).Take(20); // I believe .Take() triggers the actual database call
yourRecords = yourRecords.OrderBy(a => Guid.NewGuid()); // then randomize the items now that they are in C# memory
this turned out to be very simple using extension methods, ordering by name first, then calling Take (top on T-sql) and randomizing later
context.Categories().OrderByName().Take(20).OrderByRandom();
public static IQueryable<Category> OrderByName(this IQueryable<Category> query)
{
return from c in query
orderby c.Name
select c;
}
public static IQueryable<T> OrderByRandom<T>(this IQueryable<T> query)
{
return (from q in query
orderby Guid.NewGuid()
select q);
}

Returing multiple Model objects from LINQ Joins

I am using ASP.NET MVC framework and accessing DB records with Entities.
I am doing some joins like this:
public IQueryable<...> GetThem()
{
var ords = from o in db.Orders
join c in db.Categories on o.CategoryID equals c.ID
select new {Order=o, Category=c};
return ords;
}
I need to use/pass 'ords' from one function to other in a strongly-typed manner.
(I will be doing this kind of joins in multiple places.)
What is the best way to do this?
Do I need create a new class containing both returned vals for every join I do?
Eg: public class OrderAndCategory { public Order; public Category; } in this case.
Is there any simpler way?
Thanks!
The class representing the data in ords is strongly typed, it is generated by the compiler. You could run into problems though if the compiler generates different classes for different instances of the query, but with the same types. You'd have to check that. If this is the case, you'll have to create classes for each different query, or use the class Tuple<Targs...> instead.

Resources