I have the following piece of code which is working fine:
ObjectContext octx = new ObjectContext("name=PublisherModelContainer");
ObjectSet<Author> authorSet = octx.CreateObjectSet<Author>();
ObjectQuery<Author> q = authorSet.Where("it.FirstName == #FirstName", new ObjectParameter("FirstName", "Isaak"));
Author a = q.FirstOrDefault();
if (a == null)
{
Console.WriteLine("Author not found");
return;
}
Console.WriteLine("{0} {1}", a.FirstName, a.LastName);
While calling the 'Where' method, the FirstName property is being referenced via "it.FirstName". What does this mean? I have tried using a different alias e.g. "a.FirstName" but that fails with exception message 'a.FirstName' could not be resolved in the current scope or context.
Even in Microsoft's example here (https://msdn.microsoft.com/en-us/library/bb338811%28v=vs.110%29.aspx), it.ProductID is being used not something like t.ProductID.
What exactly is "it"? Is it that "it" has a special meaning?
It is just how you can refer to the set you're currently working with. Same as with this in C# class. But this is achieved based on the query that EF generates.
Consider the following SQL script.
SELECT Col1, Col2, Col3 FROM Table AS It
WHERE It.Col1 = #param1
Here is a link with more detailed explanation http://www.w3schools.com/sql/sql_alias.asp
Related
I have been dealing with a really frustrating EF Core (newest version) error. I'm not sure at this point if I am doing something wrong or if it's a bug. Any help the community can provide would be appreciated.
The error is in regards to Entity Framework Core and translating a LINQ expression to SQL. The below code translates to SQL properly. The query variable below could potentially have a variety of Where expressions and Includes applied to it with no issue.
// This works fine
query.Select(price => new Customer {
Name = price.Payer.Name,
Code = price.Payer.Code,
City = price.Payer.City,
ParentCode = price.Payer.ParentCode,
ParentLevel = CustomerLevel.Corporate,
CustomerLevel = CustomerLevel.Payer
}).Distinct().ToListAsync();
As soon as I add a call to OrderBy, it will not evaluate. If I remove the call to Distinct, it once again works, but I can't have both. I've tried several different ways to build the expression and several workarounds that I've found around the interwebz, and nothing seems to resolve it.
// This throws error
// query is of type IQueryable<Price>
query.Select(price => new Customer {
Name = price.Payer.Name,
Code = price.Payer.Code,
City = price.Payer.City,
ParentCode = price.Payer.ParentCode,
ParentLevel = CustomerLevel.Corporate,
CustomerLevel = CustomerLevel.Payer
}).Distinct().OrderBy(cust => cust.Name).ToListAsync();
Also, placement of the OrderBy does not seem to matter. Based on what I've read, the call to Distinct removes all prior ordering, so this one is not too surprising.
// This also throws error
// query is of type IQueryable<Price>
query
.OrderBy(price => price.payer.Name)
.Select(price => new Customer {
Name = price.Payer.Name,
Code = price.Payer.Code,
City = price.Payer.City,
ParentCode = price.Payer.ParentCode,
ParentLevel = CustomerLevel.Corporate,
CustomerLevel = CustomerLevel.Payer
}).Distinct().ToListAsync();
I actually found the cause of the issue today. The ParentCode property (used at line ParentCode = price.Payer.ParentCode) is actually a property on the base class that is not mapped to the table, so EF apparently did not know what to do with it. The very strange part of this is that it worked without the OrderBy. Changing that line to ParentCode = price.Payer.CorporateCode resolved the issue.
Thanks for anyone that took a look at this.
I've tried to reduce this example to remove the OrganizationServiceProxy/XrmServiceContext, but now I believe the issue is originating there. I'm pulling data from a Dynamics 365 instance using the code generated by CrmSvcUtil.exe from Microsoft. Here is the relevant snippet:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
CrmServiceClient client = new CrmServiceClient(userName, CrmServiceClient.MakeSecureString(password), string.Empty, orgName, isOffice365: true);
using (OrganizationServiceProxy service = client.OrganizationServiceProxy)
{
XrmServiceContext crm = new XrmServiceContext(service);
var q = crm.CreateQuery<Account>();
List<Account> accounts = q.Where(x => x.AccountNumber != x.dynamics_integrationkey).ToList();
}
I get the following exception:
variable 'x' of type '{company-specific}.Account' referenced from scope '', but it is not defined
This is happening on the last line when the Linq is actually executed. It's only when it's created through the Microsoft.Xrm.Sdk.Client.OrganizationServiceContext that I get the error.
Things I've checked:
I get the error when comparing any two fields on the same object (not just Account), even when both are the same exact data type.
If I replace crm.CreateQuery... with a hand-populated List...AsQueryable() the Linq works fine
Most frustratingly, if I work through an intermediary list with List<Account> a = q.ToList(); to rasterize the result set first then this all works fine. It takes much longer to run because I've forfeited any lazy loading, but it works without error.
I've looked at other answers related to the referenced from scope '' but not defined Linq error, but they are all about malformed Linq. I know this Linq is well formed because it works fine if I change the underlying Queryable.
Clearly something is different between a List...ToQueryable() and the Microsoft.Xrm.Sdk.Linq.Query<> created by the XrmServiceContext (which inherits CreateQuery from Microsoft.Xrm.Sdk.Client.OrganizationServiceContext), but I don't know what it is.
The query you are trying to build (compare a field value with another field value on the same entity) is not possible in Dynamics.
The LINQ Provider still uses QueryExpression under the hood, meaning that your LINQ condition will be converted in a ConditionExpression, and that comparison is not possible.
Similar questions:
https://social.microsoft.com/Forums/en-US/d1026ed7-56fd-4f54-b382-bac2fc5e46a7/linq-and-queryexpression-compare-entity-fields-in-query?forum=crmdevelopment
Dynamics QueryExpression - Find entity records where fieldA equals fieldB
This morning I realized that my answer contained the flaw of comparing two fields against each other in a LINQ query. My bad. Thank you Guido for calling that out as impossible.
I generate my proxy classes with a 3rd party tool, so I'm not familiar with the XrmServiceContext class. But, I have confirmed that XrmServiceContext inherits from Microsoft.Xrm.Client.CrmOrganizationServiceContext, which inherits from Microsoft.Xrm.Sdk.Client.OrganizationServiceContext
I use OrganizationServiceContext for LINQ queries.
If you retrieve all the accounts first by calling ToList() on the query, you can then do the comparison of the two fields. If you have too many Accounts to load all at once, you can page through them and store the mismatched ones as you go.
And, I would probably retrieve a subset of the fields rather than the whole record (as shown).
var client = new CrmServiceClient(userName, CrmServiceClient.MakeSecureString(password), string.Empty, orgName, isOffice365: true);
using (var ctx = new OrganizationServiceContext(client))
{
var q = from a in ctx.CreateQuery<Account>()
where a.AccountNumber != null
&& a.dynamics_integrationkey != null
select new Account
{
Id = a.AccountId,
AccountId = a.AccountId,
Name = a.Name,
AccountNumber = a.AccountNumber,
dynamics_integrationkey = a.dynamics_integrationkey
};
var accounts = q.ToList();
var mismatched = accounts.Where(a => a.AccountNumber != a.dynamics_integrationkey).ToList()
}
this is my first post here and I'm also new to C# / OO and LinqPAD. (translate to: I may mis-use concepts or terms due to being in 'learning mode')
I have an application that I've narrowed my bug down to the following query. I decided to try out LinqPAD to solve the issue in a constrained environment.
We are using Entity Framework for our database interactions. I am using LinqPAD's "C# Program" selection.
I found a great tutorial on how to use entities in LinqPAD and this seems to be working. (Proof: 'aEntities' does not return an error like it did when the entities were not connected properly)
So let's get to the issue.
At the 'var' I get: "NullReferenceException: Object reference not set to an instance of an object."
The reason I'm here asking this is every single example I see out there uses this 'var' approach. It does not work for some reason. I've tried declaring 'qryDC', creating a new instance of it and then assigning it. (I'm feeling weak knowledge wise here, but am reading and learning.)
I have read John Saunders epic write up about this topic. (Wow man! Thanks!)
What is a NullReferenceException, and how do I fix it?
I however was not able to translate that knowledge to address my issue (learning curve).
Here is the code:
void Main(AEntities aEntities)
{
decimal fmProjectUID = 1123;
var qryDC =
from pnp in aEntities.PNonPRs
from p in aEntities.Projects
from pt in aEntities.PurchaseTypes
from wbs in aEntities.P32
from pc in aEntities.PPhases
where pnp.ProjectUID == p.ProjectUID
where p.ProjectUID == fmProjectUID
where pnp.PurchaseTypeUID == pt.PurchaseTypeUID
where pnp.P32UID == wbs.P32UID
where pt.IsNonPR == 1
orderby pt.PurchaseTypeID
select new
{
PNonPrUID = pnp.PNonPrUID
,PurchaseTypeID = pt.PurchaseTypeID
,PhaseCode = pnp.ProjectPhase.PhaseCode
,DANbr = pnp.AType.DANbr
,PreAmt = pnp.PreAmt
,POStDate = pnp.POStDate
,POFDate = pnp.POFDate
,BaseAmt = pnp.BaseAmt
,Notes = pnp.Notes
,ChangedByName = pnp.ChangedByName
};
}
Let me know if you need more info.
And thank you for your time, effort and thoughts.
You can't just arbitrarily assign arguments to the Main method and expect them to be filled in. Your aEntities variable is null when you run this, thus the null reference exception. You need to instantiate your context.
void Main()
{
decimal fmProjectUID = 1123;
var aEntities = new AEntities();
var qryDC = from pnp in aEntities.PNonPRs
from p in aEntities.Projects
from pt in aEntities.PurchaseTypes
// etc...
}
Alternatively, if you defined your connection in LINQPad and then selected said connection for the query you can omit the creation of the AEntities and just use the following.
void Main()
{
decimal fmProjectUID = 1123;
var qryDC = from pnp in PNonPRs
from p in Projects
from pt in PurchaseTypes
// etc...
}
I would also suggest you familiarize yourself with the debugger in Visual Studio and LINQPad. Had you set a breakpoint and examined the variables you would have seen that aEntities was null.
I have a simple web application which allows an user to upload 2 .csv-files containing certain data. To persist the data I use the Entity Framework in two different Import-methods.
First Import-method
public void ImportOne(string path)
{
StreamReader sr = new StreamReader(path);
using (var db = new ContextEv("RndContext"))
{
db.Database.ExecuteSqlCommand("DELETE FROM TableA");
db.Database.ExecuteSqlCommand("DELETE FROM TableB");
while (!sr.EndOfStream)
{
string[] data = sr.ReadLine().Split(';');
string houseId = data[0];
House house = new House()
{
HouseId = houseId,
};
House dummy = db.Houses.Find(houseId);
if (!dummy.HouseId.Equals(house.HouseId))
{
db.Houses.Add(house);
}
}
}
}
}
This line fails: House dummy = db.Houses.Find(houseId); with the following exception:
The type of one of the primary key values did not match the type
defined in the entity. See inner exception for
details.\r\nParametername: keyValues
ErrorContext of InnerException:
keyword 'AS', line 1, column 22
ErrorDescription of InnerException:
The query syntax is not valid.
Alright, I checked if really the type is the problem here. However I haven't found anything wrong.
The "funny" thing about it is, that I use the same Find-method in another Import-method and it works without any exception!
using (var db = new ContextEv("RndContext"))
{
db.Database.ExecuteSqlCommand("DELETE FROM TableC");
db.Database.ExecuteSqlCommand("DELETE FROM TableD");
StreamReader sr = new StreamReader(path);
while (!sr.EndOfStream)
{
string[] data = sr.ReadLine().Split(';');
string houseId = data[5];
House house = db.Houses.Find(houseId);
...
...
db.SaveChanges();
}
}
I wasn't sure which code is really needed for you to answer my question but I'd be very happy to post more if someone asks for a particular code.
UPDATE 1 ANSWER TO user89861
'db.Houses.ToList().Find(h => h.HouseId == houseId)' threw an
exception of type 'System.NullReferenceException'
" bei
System.Data.Entity.Internal.Linq.InternalQuery1.GetEnumerator()\r\n
bei System.Data.Entity.Internal.Linq.InternalSet1.GetEnumerator()\r\n
bei
System.Data.Entity.Infrastructure.DbQuery1.System.Collections.Generic.IEnumerable<TResult>.GetEnumerator()\r\n
bei System.Collections.Generic.List1..ctor(IEnumerable1
collection)\r\n bei
System.Linq.Enumerable.ToList[TSource](IEnumerable1 source)"
Find() returns null if result is not found, so you should validate the variable house before accesssing its members:
House house = db.Houses.Find(obj => {return obj.HouseId == houseId;});
if (house == null) continue; //go to next iteration
I managed to fix this weird bug. In the Context-Constructor I have simply added
public ContextEv(string dbName) : base("name=" + dbName)
{
//Database.SetInitializer(new DropCreateDatabaseAlways<ContextEv>());
}
However at first I had to delete every table manually because the code above didn't really do it. And after letting it run once I had to comment the code and run again so the table appear (why, I really do not know.. maybe some of you know).
Thank you everyone very much for your help! I really learned some things with your answers.
The .find() method is not strongly typed, it uses object parameters and hence the error you describe occur during runtime, if the data type of the parameters passed to it are incompatible (e.g. you pass a string where an integer value is expected).
Generally, I usually prefer to use .Where() and / or .FirstOrDefault() rather than .Find(), because you have to specify the field names, e.g.
var usr=db.Employees.Where(
x => x.FirstName=="James" && x.LastName=="Bond").FirstOrDefault();
or you could directly write:
var usr=db.Employees.FirstOrDefault(
x => x.FirstName=="James" && x.LastName=="Bond");
Both return Null if no records were found.
It makes the code more clear for later reviews and possibly required data model changes - consider if you have to add a field to a compound primary key, such as birth date in the example above: In this case it is easy to see that you have to add it to the .Where statement.
Because it is strongly-typed, it also allows you to use intellisense to look up the proper data type of the fields involved in the query by right-clicking and selecting "go to definition."
All these benefits make trouble-shooting much easier. Also, .Where and .FirstOrDefault are more versatile than .Find, because they support multiple kinds of sequences (look here at SO for a more detailed explanation) and are not restricted to primary keys.
The downside is that .Any(), .Single(), .First(), .Where() (and their ...OrDefault() pendents) are generating SQL queries and hence roundtrips to the database, in a similar way as if you would use ad-hoc queries or call stored procedures.
However, if you're not searching within the primary key, you have to use them. The tool LinqPad shows nicely how such queries are translated by EF into SQL code (assuming Northwind as example database):
SELECT TOP (1)
[Extent1].[EmployeeID] AS [EmployeeID], -- primary key (PK)
[Extent1].[LastName] AS [LastName], [Extent1].[FirstName] AS [FirstName],
[Extent1].[Title] AS [Title], [Extent1].[TitleOfCourtesy] AS [TitleOfCourtesy],
[Extent1].[BirthDate] AS [BirthDate], [Extent1].[HireDate] AS [HireDate],
[Extent1].[Address] AS [Address], [Extent1].[City] AS [City],
[Extent1].[Region] AS [Region],
[Extent1].[PostalCode] AS [PostalCode], [Extent1].[Country] AS [Country],
[Extent1].[HomePhone] AS [HomePhone], [Extent1].[Extension] AS [Extension],
[Extent1].[Photo] AS [Photo], [Extent1].[Notes] AS [Notes],
[Extent1].[ReportsTo] AS [ReportsTo], [Extent1].[PhotoPath] AS [PhotoPath]
FROM [dbo].[Employees] AS [Extent1]
WHERE (N'James' = [Extent1].[FirstName]) AND (N'Bond' = [Extent1].[LastName])
Here you can see that it makes sense to restrict the fields in the resultset wherever possible, otherwise the generated query will return values you're not interested in and return an unnecessarily high amount of data.
The .Find() method will only work here with the EmployeeID, since that is the primary key (PK). For all other fields involved in queries, you can't use .Find() and have to use the other query methods (.Where(), .Single(), .First() or .Any()).
In your particular case it would look like (note you should only create a new object if required, so I have moved that to the if statement):
string houseId = data[0];
House dummy = db.Houses.FirstOrDefault(x=>x.HouseId==houseId);
if (dummy==null)
{
House house = new House()
{
HouseId = houseId
};
db.Houses.Add(house);
}
But note in this case, it can be further optimized by using .Any():
string houseId = data[0];
if (!db.Houses.Any(x => x.HouseId == houseId))
{
House house = new House()
{
HouseId = houseId,
};
db.Houses.Add(house);
}
if you don't need the to retrieve the object from database anyway, which avoids returning unnecessary data (as mentioned before).
What I have:
DB with data and connection to it by
public class VMMExtDBContext : DbContext {...}
inside this perfectly works something like this:
public DbSet<VirtualMachine> VirtualMachines { get; set; }
now I need a run query that perfectly run from MSSMS:
select SUM(CPUCount), SUM(RAMSizeGB), SUM(PrimaryHDDVolumeGB)
from TempVMs
where VMCloudID='f51b73b5-fdb3-4af2-956b-b27fccc5e19d'
So I wrote code:
public QuotaPresenter(VMMExtDBContext db, Guid CloudID)
{
string entitySQL = "select SUM(CPUCount), SUM(RAMSizeGB), SUM(PrimaryHDDVolumeGB) from TempVMs where VMCloudID='"+CloudID.ToString()+"'";
//or = "select SUM(CPUCount), SUM(RAMSizeGB), SUM(PrimaryHDDVolumeGB) from dbo.TempVMs where VMCloudID='"+CloudID.ToString()+"'";
var objectContext = (db as IObjectContextAdapter).ObjectContext;
var query=objectContext.CreateQuery<UsedQuotaValue>(entitySQL);
query.MergeOption = System.Data.Objects.MergeOption.NoTracking;
var QuoteValues=query.ToList(); //<--Exception here
if (QuoteValues.Count > 0) {
//На случай если нет ни одной виртуальной машины - оставляем по нулям
UQV.CPU = QuoteValues[0].CPU;
UQV.RAM = QuoteValues[0].RAM;
UQV.PrimaryHDDGB = QuoteValues[0].PrimaryHDDGB;
}
}
And when I run got an exception:
[System.Data.EntitySqlException]
'TempVMs' could not be resolved in
the current scope or context. Make sure that all referenced variables
are in scope, that required schemas are loaded, and that namespaces
are referenced correctly. Near simple identifier, line 1, column
68.
What am I missing?
I think the problem is you are missing the schema indentifier from the table name. And the query will not work without a group by statement if i am correct.
I think the problem is that you are trying to use regular transact-sql in place of entity sql, and they actually are not the same. Take a look at Aggregate Functions, this may help you (I cannot test it myself)
SELECT VALUE SqlServer.SUM(p.ListPrice)
FROM AdventureWorksEntities.Products as p