How to loop an IList of objects and set state to modified for SQL - sql

I am trying to loop a list of objects and set them as modified before posting to SQL. but I am getting an error Object Reference not set to an instance of an object.
setting a single instance of object works fine using
[BindProperty]
public Models.Ord Order { get; set; }
Order = await _context.Ord.SingleOrDefaultAsync(m => m.Id == id);
_context.Attach(Order).State = EntityState.Modified;
But this return an error.
[BindProperty]
public IList<OrdLn> OrderLineList { get; private set; }
OrderLineList = await _context.OrdLn.Where(o => o.OrdId == id).ToListAsync();
foreach (OrdLn p in OrderLineList)
{
_context.Attach(p).State = EntityState.Modified;
}
await _context.SaveChangesAsync();

The 'Q' in LINQ stands for "Query". LINQ is not meant to update objects.
You can use LINQ to find the object you want to update and then update it "traditionally".
When you are using SingleOrDefault function, the object in remote or database until you make changing the entity type(changing to new class), so there you can make change on the entity and db operation able to carryout.
When you are using List function. the object is in memory of the application so there is relation between db and will not able to carryout db operate operation.
All of these return types have interfaces inherit from IEnumerable, which you should make sure you understand. That interface basically lets you use the class in a foreach statement (in C#).
IList : is everything that ICollection is, but it also supports adding and removing items, retrieving items by index, etc. It's the most commonly-used interface for "lists of objects", which is vague I know.
IQueryable : is an enumerable interface that supports LINQ. You can always create an IQueryable from an IList and use LINQ to Objects, but you also find IQueryable used for deferred execution of SQL statements in LINQ to SQL and LINQ to Entities.

Related

SQL-Query returns 'sql-select' string instead of record-set

I'm querying a SQL table via an API and I expect the API to return a <IQueryable> collection of 'Outfit' objects however, the response object is actually just a string containing the sql-select statement.
Questions:
Why is the response object returning the SQL-select statement as a string?
Should the IQueryable<> contain a collection of 'Outfit' objecy-types? And should the object-type 'Outfit' be a C# class OR can I use a generic type like 'object' to hold each 'outfit' in the collection?
//Endpoint setup within the APIcontroller.cs file
[Route("api/getSummerOutfits")]
[HttpGet]
[Authorize]
public string getSummerOutfits()
{
IQueryable<Outfit> outfits = _dbContext.Outfits.Where(outfit => outfit.Type == 'Summer');
return outfits;
}
//Setup for the service within the api.service.ts file
getSummerOutfits(): Observable<Object>
{
return this.httpClient.get('/api/getSummerOutfits').pipe();
}
//A snippet of the response-string when the API is called within Postman
"SELECT \r\n ....... WHERE Outfit.Type = 'Summer'"
I have tried setting the IQueryable<> to contain objects of-type 'outfit' however the response continues to be a string containing the sql-select statement.
The query is declared but never executed.
IQueryable<T> (Remarks section)
Enumeration causes the expression tree associated with an IQueryable object to be executed.
Queries that do not return enumerable results are executed when the Execute method is called.
You have to materialize the query with .ToList() or .AsEnumerable().
public List<Outfit> getSummerOutfits()
{
List<Outfit> outfits = _dbContext.Outfits
.Where(outfit => outfit.Type == 'Summer')
.ToList();
return outfits;
}
While I suggest removing.pipe() as you didn't perform any operation in the response. And return the value of Observable<any[]> or Observable<Outfit[]> if you have write Outfit class/interface.
getSummerOutfits(): Observable<any[]>
{
return this.httpClient.get<any[]>('/api/getSummerOutfits');
}
I'm surprised that even worked. Essentially it passed back an IQueryable<Outfit>.ToString() result.
To return a collection of Outfits Yong Shun's answer covers that using a ToList() and having the return type being an IEnumerable<Outfit>/ ICollection<Outfit>.
As a general rule though I don't recommend passing entities back to views or API, especially for asynchronous AJAX calls as this will typically send far more information than the consumer needs, and potentially opens you up to serialization "traps" with lazy loading.
Instead, define a view model or a DTO, which is a serializable POCO C# object containing just the fields your consumer needs. Then your method becomes:
public IEnumerable<OutfitDto> getSummerOutfits()
{
var outfits = _dbContext.Outfits
.Where(outfit => outfit.Type == 'Summer')
.Select(outfit => new OutfitDto
{
// copy values across here.
}).ToList();
return outfits;
}
This avoids the possibility that a serializer attempts to lazy load navigation properties in Outfit. It reduces the size of the payload by just including the fields that you need, and removes any need to eager-load entire related entities if there are details you want from those. (just reference the related entity properties, EF will build the suitable query) It also avoids confusion especially if you go to pass an outfit DTO back to the server rather than attempting to send what you think is an Entity which is actually nothing more than a serialized JSON object or set of parameters type-cast into an Entity class.

IQueryable with Where containing a list throws "No mapping to a relational type can be found for the CLR type 'List<string>'."

While using IBM.EntityFrameworkCore and IBM.Data.DB2.Core an error is being thrown everytime when I have a Where statement containing a list on which some kind of verification is being performed.
The error is being thrown when the IQueryable is being materialized i.e. converted to a list using ToList().
No mapping to a relational type can be found for the CLR type
'List'.
private IQueryable<CollectionActivityInOverview> FilterActivitiesForAdvisor(IQueryable<CollectionActivityInOverview> activitiesList, List<string> advisorCodes)
{
List<AdvisorCustomerInfo>advisorInformation = GetAdvisorCustomers(advisorCodes).Result;
var advisorCustomers = advisorInformation.SelectMany(a => a.Customers)
.Distinct()
.ToList();
// This is the problematic spot
activitiesList = activitiesList.Where(a => advisorCustomers.Any(ac => ac == a.CustomerId));
return activitiesList;
}
.
public class AdvisorCustomerInfo
{
public string AdvisorCode { get; set; }
public List<string> Customers { get; set; }
}
What could the error be, since AFAIK, conditions using lists should not only be supported by Linq to Entities, but also supported by Linq to SQL.
Is it possible that DB2 Entity Framework library doesn't support i.e. doesn't translate such conditions into SQL?
Can someone confirm it?
My main requirement is to keep IQueryable in order to execute the query against the database. Is it possible to rewrite the query using something else?
It is true that DB2 EF Core Library (at the moment) doesn't support using of .Any() or .Contains(). It is, however, possible to "override" this behaviour by writing a custom Expression that mimics SQL WHERE function with multiple conditions in the query.
Basically you pass the list you want to iterate and you append each item of the list to the Expression body using Expression.OrElse(). At the end you return the expression using Expression.Call().

LINQ query take(count) from collection of child elements

How can I make LINQ query (I am using Entity Framework) that returns top n elements in a child collection?
Here are example classes
public class A {
public int ID
public ICollection<B> bData
}
public class B {
public int ID
public string Name
}
This is kind of query I was thinking;
db.A.Where(a => a.ID == query_id).Include(a => a.bData.Take(count)).ToList();
Normally this doesn't work, how could I accomplish this?
The Include method is intended for eager loading the related entity data. It's all or nothing and cannot be used for filtering, sorting, grouping etc.
So what are you asking for can be accomplished with projection (select). It would be simpler if you project to anonymous or custom type (a.k.a. DTO object), because EF does not allow projecting to entity type. But it's still doable by using anonymous type projection in LINQ to Entities query and doing second projection in LINQ to Objects to attach the filtered child collection to its parent entity like this:
var result = db.A
.Where(a => a.ID == query_id)
.Select(a => new { a, bData = a.bData.Take(count).ToList() })
.AsEnumerable() // Switch to LINQ to Object context
.Select(x =>
{
x.a.bData = x.bData;
return x.a;
})
.ToList();
Please note that in order the above to work, bData member should not be virtual or db.Configuration.LazyLoadingEnabled should be false (i.e. lazy loading should be off, otherwise the bData collections will be fully reloaded when first accessed).

Linq to SQL - Attribute based mapping - cannot instantiate new object with no arguments

I want to extend Linq's DataContext class to implement the ORM. Currently my model looks like this:
public class Trial : DataContext
{
public Trial(string connectionString) : base(connectionString) { }
[Column(DbType = "System.Guid", IsPrimaryKey = true, IsDbGenerated = true, CanBeNull = false)]
public Guid TrialID { get; set; }
//...
}
However when I try to instantiate a new Trial object to insert it into the database I get an error complaining that Trial does not have a constructor that takes 0 arguments. When I try to create such a constructor, VS complains that DataContext does not have a constructor that takes 0 arguments.
Am I missing something here? How do I seperate the data context from the model definition?
(First time using Linq!)
Thanks in advance,
Max.
Your data context that represents the database view should inherit from DataContext. It should expose Tables where T is the entities (rows) that you want to add. Try generating a model from the database using the designer or SQLMetal and take a closer look at the generated code to see what's going on.

Returning datasets from LINQ to SQL in a REST/WCF service

I have a WCF/REST web service that I'm considering using Linq to SQL to return database info from.
It's easy enough to do basic queries against tables and return rows, for example:
[WebGet(UriTemplate = "")]
public List<User> GetUsers()
{
List<User> ret = new List<User>(); ;
using (MyDataContext context = new MyDataContext())
{
var userResults = from u in context.Users select u;
ret = userResults.ToList<User>();
}
return ret;
}
But what if I want to return data from multiple tables or that doesn't exactly match the schema of the table? I can't figure out how to return the results from this query, for example:
var userResults = from u in context.Users
select new { u.userID, u.userName, u.userType,
u.Person.personFirstname, u.Person.personLastname };
Obviously the resulting rowset doesn't adhere to the "User" schema, so I can't just convert to a list of User objects.
I tried making a new entity in my object model that related to the result set, but it doesn't want to do the conversion.
What am I missing?
Edit: related question: what about results returned from stored procedures? Same issue, what's the best way to package them up for returning via the service?
Generally speaking, you shouldn't return domain objects from a service because if you do you'll run into issues like those you're finding. Domain objects are intended to describe a particular entity in the problem domain, and will often not fit nicely with providing a particular set of data to return from a service call.
You're best off decoupling your domain entities from the service by creating data transfer objects to represent them which contain only the information you need to transfer. The DTOs would have constructors which take domain object(s) and copy whatever property values are needed (you'll also need a parameterless constructor so they can be serialized), or you can use an object-object mapper like AutoMapper. They'll also have service-specific features like IExtensibleDataObject and DataMemberAttributes which aren't appropriate for domain objects. This frees your domain objects to vary independently of objects you send from the service.
You can create a Complex Type and instead of returning Anonymous object you return the Complex Type. When you map stored procedures using function import, you have a option to automatically create a complex type.
Create a custom class with the properties that you need:
public class MyTimesheet
{
public int Id { get; set; }
public string Data { get; set; }
}
Then create it from your Linq query:
using (linkDataContext link = new linkDataContext())
{
var data = (from t in link.TimesheetDetails
select new MyTimesheet
{
Id = t.Id,
Data = t.EmployeeId.ToString()
}).ToList();
}