async and anonymous types - c#

This method:
private async Task readFileInfo(string folderId)
Has a call to another method:
importCount = await VM.importVehicles(myXDoc);
Defined here: (note: I changed it from a for loop to a for each but I'm getting similar results).
public async Task<Int32> importVehicles(XDocument importXMLDocument)
{
var Importedvehicles = from vehicle in importXMLDocument.Descendants("vehicle")
select new
{
VehicleName = vehicle.Element("VehicleName").Value,
VehicleYear = vehicle.Element("VehicleYear").Value,
Odometer = vehicle.Element("Odometer").Value,
LicensePlate = vehicle.Element("LicensePlate").Value,
OilWeight = vehicle.Element("OilWeight").Value,
OilQuantity = vehicle.Element("OilQuantity").Value,
OilFilterModelNumber = vehicle.Element("OilFilterModelNumber"),
AirFilterModelNumber = vehicle.Element("AirFilterModelNumber"),
OilChangedDate = vehicle.Element("OilChangedDate"),
OilChangedOdometer = vehicle.Element("OilChangedOdometer"),
NextOilChangeDate = vehicle.Element("NextOilChangeDate"),
NextOilChangeOdometer = vehicle.Element("NextOilChangeOdometer"),
SettingDistance = vehicle.Element("SettingDistance"),
SettingMonths = vehicle.Element("SettingMonths"),
};
Int32 vehicleId;
vehicleId = await getMaxVehicleId();
try
{
foreach (var item in Importedvehicles)
{
vehicle myImportedVehicle = new vehicle();
myImportedVehicle.VehicleId = vehicleId += 1;
myImportedVehicle.ImagePath = "Assets/car2.png";
myImportedVehicle.VehicleName = item.VehicleName;
myImportedVehicle.VehicleModel = item.VehicleName;
myImportedVehicle.VehicleYear = short.Parse(item.VehicleYear);
myImportedVehicle.CurrentOdometer = Convert.ToInt32(item.Odometer);
myImportedVehicle.LicensePlate = item.LicensePlate;
myImportedVehicle.LastOilChangedDate = Convert.ToDateTime(item.OilChangedDate.Value.ToString()).ToString("d");
myImportedVehicle.LastOilChangedOdometer = (Int32)item.OilChangedOdometer;
myImportedVehicle.ReminderDistance = (Int32)item.SettingDistance;
myImportedVehicle.ReminderMonths = (Int32)item.SettingMonths;
vehicleInformation myImportVI = new vehicleInformation();
myImportVI.OilWeight = item.OilWeight;
myImportVI.OilAmount = item.OilQuantity;
myImportVI.OilFilterNumber = item.OilFilterModelNumber.Value.ToString();
myImportVI.AirFilterNumber = item.AirFilterModelNumber.Value.ToString();
myImportedVehicle.vehicleInfo = myImportVI;
m_vehicles.Add(myImportedVehicle);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message.ToString());
}
await SaveList();
return Importedvehicles.Count();
}
I'm getting an error:
Object reference not set to an instance of an object.
when I step through it the iVehicle is highlighted but then it goes directly to the for statement. Then it errors as it looks as it hasn't gotten the results from iVehicle yet.

This doesn't 100% answer your question, but it should give you a good start.
The reason the debugger jumps straight into the for...loop after the declaration of iVehicle, is because your query does not get executed when you declare it. Therefore, iVehicle at that point is not a collection of anonymous types.
When you call .Count(), the query is being executed and iVehicle is attempting to be turned into a proper collection of anonymous types. However, because something in the query (that is being executed after you call .Count()) is null, you're receiving an NullReferenceException.
You should start by verifying that both importXMLDocument and the return value from the call to Descendants() is not null.
Hope that helps anyway.
EDIT:
Now that you've given a complete example, you have heaps of places that could potentially be null.
Each time you use this:
vehicle.Element("SomeElementNameHere")
That could potentially be null. Then you're calling the .Value property on a null object.
You need to make sure each element is definitely there. Isolate each case, and determine which one is null.

Try writing your code like this:
var query =
from vehicle in importXMLDocument.Descendants("vehicle")
select new { ... };
var iVehicle = query.ToArray();
for (var i = 0; i <= iVehicle.Count(); i++)
{
...
}
You need to force the evaluation of the query. That's what the .ToArray is doing. The query itself is only the definition of the query, not the results.

Related

How to get object in c#

ObjetosAuxiliar.ReservaServicoResumo resumoServico = new ObjetosAuxiliar.ReservaServicoResumo();
resumoServico.Quantidade = this.ReservaServico.Sum(r => r.Quantidade);
resumoServico.ValorTotal = this.ReservaServico.Sum(r => r.Quantidade * r.Servico.Valor);
resumoServico.Servico = r.Servico; --WRONG
return resumoServico;
How to get in resumoServico.Servico the object r.Servico?
I think you are misunderstanding something.
r is not a real object (per se), is a representation that you use for the ReservaServico in order to use the Sum method, r only exists inside the call for that method.
So, I think what you are looking for is:
resumoServico.Servico = this.ReservaServico.Servico;
UPDATE
If ReservaServico is a collection, the only way of get the values is to store them in a collection, if this is the case you can do it this way.
ObjetosAuxiliar.ReservaServicoResumo resumoServico = new ObjetosAuxiliar.ReservaServicoResumo();
resumoServico.Quantidade = this.ReservaServico.Sum(r => r.Quantidade);
resumoServico.ValorTotal = this.ReservaServico.Sum(r => r.Quantidade * r.Servico.Valor);
foreach(var r in ReservaServico)
{
resumo.Servico.Add(r.Servico);
}
For this to work, resumo.Servico has to be a collection of ReservaServico.Servico, if you can't change the type you can't have the collection of Servico.

What could be the reason my base entity is not loaded in extension method?

I am having difficulty in Kendo Grid and Linq usage.I am using kendo only in backend to simplfiy filtering and sorting.Everything was working fine until I touched an objet which has related objects.My objects base objects not loaded in my extension method but if I use object initilization in linq select method it works fine. Maybe my linq knowledge is not enough but I want to learn what is the trick happening here ? Any help would be greatly appreciated.
My Extension Method Which gives me the ViewModel :
public static MeetingRoomCultureListViewModel ToMeetingRoomCultureListViewModel(this MeetingRoomCulture meetingRoomCulture) {
var viewModel = new MeetingRoomCultureListViewModel();
viewModel.Id = meetingRoomCulture.BaseEntityId;
viewModel.CancellationDuration = meetingRoomCulture.BaseEntity.CancellationDuration;
viewModel.MinimumMeetingDuration = meetingRoomCulture.BaseEntity.MinimumMeetingDuration;
viewModel.OnlyAdminsCanReserve = meetingRoomCulture.BaseEntity.OnlyAdminsCanReserve;
viewModel.Price = meetingRoomCulture.BaseEntity.Price;
viewModel.SaltoLockId = meetingRoomCulture.BaseEntity.SaltoLockId;
viewModel.Status = meetingRoomCulture.Status;
viewModel.Capacity = meetingRoomCulture.BaseEntity.Capacity;
viewModel.CleaningDuration = meetingRoomCulture.BaseEntity.CleaningDuration;
viewModel.Color = meetingRoomCulture.BaseEntity.Color;
viewModel.Currency = meetingRoomCulture.BaseEntity.Currency;
viewModel.IsHidden = meetingRoomCulture.BaseEntity.IsHidden;
viewModel.LocationId = meetingRoomCulture.BaseEntity.LocationId;
viewModel.LocationName = meetingRoomCulture.BaseEntity.Location.Name;
viewModel.MaximumDaysForReservationInFuture = meetingRoomCulture.BaseEntity.MaximumDaysForReservationInFuture;
viewModel.PictureUrl = meetingRoomCulture.BaseEntity.PictureUrl;
viewModel.Name = meetingRoomCulture.BaseEntity.Name;
viewModel.TaxRatio = meetingRoomCulture.BaseEntity.TaxRatio;
viewModel.MaximumMeetingDuration = meetingRoomCulture.BaseEntity.MaximumMeetingDuration;
viewModel.CreatedDate = meetingRoomCulture.CreatedDate;
viewModel.LastModifiedDate = meetingRoomCulture.LastModifiedDate;
return viewModel;
}
Here this BaseEntity is not loading and I get null reference exception.
And my api method :
public IActionResult Get([FromQuery] [DataSourceRequest] DataSourceRequest request) {
var ds = _dbContext.MeetingRoomCultures.AsNoTracking().Include(x => x.BaseEntity).ThenInclude(x => x.Location).CultureFilter(CurrentCulture)
.Select(x => x.ToMeetingRoomCultureListViewModel()).ToDataSourceResult(request);
return Ok(ds);
}
However If I use object initialization everything works fine and Base Entity is loaded no exception happens .Like below :
public IActionResult Get([FromQuery] [DataSourceRequest] DataSourceRequest request) {
var ds = _dbContext.MeetingRoomCultures.AsNoTracking().Include(x => x.BaseEntity).ThenInclude(x => x.Location).CultureFilter(CurrentCulture)
.Select(x => new MeetingRoomCultureListViewModel()
{
Id = x.BaseEntityId,
CancellationDuration = x.BaseEntity.CancellationDuration,
MinimumMeetingDuration = x.BaseEntity.MinimumMeetingDuration,
OnlyAdminsCanReserve = x.BaseEntity.OnlyAdminsCanReserve,
Price = x.BaseEntity.Price,
SaltoLockId = x.BaseEntity.SaltoLockId,
Status = x.Status,
Capacity = x.BaseEntity.Capacity,
CleaningDuration = x.BaseEntity.CleaningDuration,
Color = x.BaseEntity.Color,
Currency = x.BaseEntity.Currency,
IsHidden = x.BaseEntity.IsHidden,
LocationId = x.BaseEntity.LocationId,
LocationName = x.BaseEntity.Location.Name,
MaximumDaysForReservationInFuture = x.BaseEntity.MaximumDaysForReservationInFuture,
PictureUrl = x.BaseEntity.PictureUrl,
Name = x.BaseEntity.Name,
TaxRatio = x.BaseEntity.TaxRatio,
MaximumMeetingDuration = x.BaseEntity.MaximumMeetingDuration,
CreatedDate = x.CreatedDate,
LastModifiedDate = x.LastModifiedDate
}).ToDataSourceResult(request);
return Ok(ds);
}
There are several reasons.
First, both queries use projection (Select) to non entity type, hence fall into Ignored includes category:
If you change the query so that it no longer returns instances of the entity type that the query began with, then the include operators are ignored.
The difference is where the Select is evaluated. Custom (extension) methods cannot be translated to SQL, hence are executed on the client. Since includes are ignored, you get null reference navigation properties and null or empty collection navigation properties. While in the second scenario the query is translated to SQL and executed server (database) side. There are no real "objects" or "collections" involved inside the SQL query, just tables and joins.
For more info, see Client vs. Server Evaluation abd How Queries Work (and basically the whole Querying Data related) documentation topics.
To recap, for performance (and many other reasons), always try to create server side queries. Which means not using custom methods at all. If you need to reuse the logic, put it inside Expression<TSource, TResult>, compile a delegate and use it from elsewhere.
For instance:
public static class Selectors
{
public static readonly Expression<Func<MeetingRoomCulture, MeetingRoomCultureListViewModel>>
MeetingRoomCultureToListViewModel = source => new MeetingRoomCultureListViewModel
{
Id = source.BaseEntityId,
CancellationDuration = source.BaseEntity.CancellationDuration,
// the rest ...
};
private static readonly Func<MeetingRoomCulture, MeetingRoomCultureListViewModel>
MeetingRoomCultureToListViewModelFunc = MeetingRoomCultureToListViewModel.Compile();
public static MeetingRoomCultureListViewModel ToMeetingRoomCultureListViewModel(
this MeetingRoomCulture source) => MeetingRoomCultureToListViewModelFunc(source);
}
And of course use the expression inside LINQ to Entities queries:
var ds = _dbContext.MeetingRoomCultures // no tracking, no includes
.CultureFilter(CurrentCulture)
.Select(Selectors.MeetingRoomCultureToListViewModel) // <--
.ToDataSourceResult(request);
As mentioned in the comments, 3rd party libraries like AutoMapper can greatly simplify the transformations from / to entity models.

How to determine what object rise InvalidCastException in class assignment?

I have a class assignment like that:
Variable currentVar = new Variable() {
Id = var.Field<int>("id"),
Address = var.Field<string>("address"),
Matricola = var.Field<int>("matricola"),
MachModel = var.Field<string>("modello"),
MachType = var.Field<string>("machinetype"),
Part = var.Field<string>("part"),
PartNumber = var.Field<int>("partnumber"),
PartMeasurement = var.Field<string>("partmeasurement"),
Priority = var.Field<int>("priority") ,
EnableEventHub = var.Field<bool>("enableeventhub"),
MinAgeEventHub = var.Field<int>("minageeventhub")};
Sometime it rise an InvalidCastException. When I debug the application I cannot see what "var" values are because they're in another context, and the line reference on the stack report the first because it is only one statement.
What is a good way to determine what field of "var" gives me the exception?
My block is in a normal try catch and I log the exception message and stack.
You could try to assign all data before you declare the class.
Like that:
int iMatricola = var.Field<int>("matricola");
string strAddress = var.Field<string>("address");
Variable currentVar = new Variable() {
Address = strAddress,
Matricola = iMatricola
};
I prefer this way if I have to use not type-safe values.

Generic dapper QueryMultiple function

I am using asp.net mvc, dapper and MySql stored procedures for my web app.
So far, for each page I have 1-3 stored procedures calls.
I recently found out my hosting only provides 10 parallel connections and so I want to combine my calls into one per page.
I already made the necessary stored procedures in the database, but my problem is using dapper in generic way to get the procedures results.
What I want is to make a generic function that will get:
1) Stored Procedure name.
2) List of types the stored procedure returns. And for each type if it is Single/ToList.
The generic function should result a list of variablese that are the results from the stored procedure.
The example in dapper page shows how to make a non-generic QueryMultiple call:
var sql =
#"
select * from Customers where CustomerId = #id
select * from Orders where CustomerId = #id
select * from Returns where CustomerId = #id";
using (var multi = connection.QueryMultiple(sql, new {id=selectedId}))
{
var customer = multi.Read<Customer>().Single();
var orders = multi.Read<Order>().ToList();
var returns = multi.Read<Return>().ToList();
...
}
Basicly, what I want is to insert the Customer,Order,Return types into a dictionary that will state each of them to be Single or ToList, and then do something like:
var result = new List<dynamic>();
var sql =
#"
select * from Customers where CustomerId = #id
select * from Orders where CustomerId = #id
select * from Returns where CustomerId = #id";
using (var multi = connection.QueryMultiple(sql, new {id=selectedId}))
{
foreach(var item in dictionary)
{
if (item.Value.equals("Single"))
result.Add(multi.Read<item.Key>().Single());
else if (item.Value.equals("ToList"))
result.Add(multi.Read<item.Key>().ToList());
}
}
return result;
My problem is, visual studio says:
The type or namespace name 'item' could not be found
Pointing at 'item' in: multi.Read()
What am I doing wrong?
Is there a better way to implement a generic function that uses dapper's QueryMultiple?
It's an old topic though but thought it might help others. In that case you can use below code to make it work. Pass type in Read function as parameter. You can return dynamic. Using Expando Object you can generate properties dynamically.
I would prefer to create object like below and pass list of object to function which will generate dynamic return type for you.
public class MapItem
{
public Type Type { get; private set; }
public DataRetriveTypeEnum DataRetriveType { get; private set; }
public string PropertyName { get; private set; }
public MapItem(Type type, DataRetriveTypeEnum dataRetriveType, string propertyName)
{
Type = type;
DataRetriveType = dataRetriveType;
PropertyName = propertyName;
}
}
//And then make a reusable function like below
public async Task<dynamic> ExecuteQueryMultipleAsync(IDbConnection con, string spName, object param = null,IEnumerable<MapItem> mapItems = null)
{
var data = new ExpandoObject();
using (var multi = await con.QueryMultipleAsync(spName,param, commandType:CommandType.StoredProcedure))
{
if (mapItems == null) return data;
foreach (var item in mapItems)
{
if (item.DataRetriveType == DataRetriveTypeEnum.FirstOrDefault)
{
var singleItem = multi.Read(item.Type).FirstOrDefault();
((IDictionary<string, object>) data).Add(item.PropertyName, singleItem);
}
if (item.DataRetriveType == DataRetriveTypeEnum.List)
{
var listItem = multi.Read(item.Type).ToList();
((IDictionary<string, object>)data).Add(item.PropertyName, listItem);
}
}
return data;
}
}
Below how you call the above method:
var mapItems = new List<MapItem>()
{
new MapItem(typeof(YourObject1), DataRetriveTypeEnum.FirstOrDefault, "Object1"),
new MapItem(typeof(YourObject2), DataRetriveTypeEnum.List, "Object2")
};
var response = await ExecuteQueryMultipleAsync(name, request, mapItems);
var object1 = response.Result.Object1;
var listOfObject2 = ((List<dynamic>)response.Result.Object2).Cast<YourObject2>();
Hope this helps.
Cheers
I think an alternative approach could be instead of QueryMultiple() method, you can make use of
IDbConnection.ExecuteReader()
extension method from Dapper.SqlMapper which returns IDataReader and you can loop through it and get the result sets by reader.NextResult() and map them to your object.

Dynamic LINQ: Specifying class name in new clause

With Dynamic LINQ, what changes need to be done to have fields of the given class?
For example, how can the following C# query be reproduced in DLinq:
var carsPartial = cars.Select(c => new {c.year, c.name, make = new maker() {name = c.make.name} }).ToList();
I have applied the changes mentioned in this answer https://stackoverflow.com/a/1468357/288747 to allow the return type to be the calling type rather than an anonymous type.
With the class definition is as follows (if it helps):
class car
{
public int vid;
public int odo;
public int year;
public string name;
public maker make;
}
class maker
{
public string name;
public int firstYear;
}
The following doesn't work (but I think is close, but still doesn't work as I don't have the changes necessary to the dynamic linq library, which is what I need):
var carsPartial = cars.Select("new(name, year, new maker() {name = make.name})").ToList();
But it fails at the new maker() { (as expected).
I'm sure I need to change the DynamicLibrary.cs to get this working and could do with some direction on how to alter it to achieve this.
UPDATE: I have turned my answer into a little bit more extensive blog post.
I have not really ever used Dynamic Linq library, but I have taken a look at the DynamicLibrary.cs code and the change to support generating type classes provided in another stackoverflow question you provided link to in your question. Analyzing them all, it seems that the nested new-s should work out of the box in your configuration.
However, it seems your query is not the correct Dynamic Linq's language query. Note, that the query string for DLinq is not equivalent to C# and has its own grammar.
The query should read out, I believe, the following:
var carsPartial = cars.Select("new(name, year, new maker(make.name as name) as make)").ToList();
EDIT:
Rereading this stackoverflow question more carefully, I realizes, that it actually does not extend the Dynamic Linq's language with the possibility for creating new strong-typed classes. They just put the result to the class specified as a generic parameter of Select() instead of specifying it in the query string.
To obtain what you need you will need to revert their changes (get generic DLinq) and apply my changes, I have just verified to work:
Locate the ParseNew method of ExpressionParser class and change it to the following:
Expression ParseNew() {
NextToken();
bool anonymous = true;
Type class_type = null;
if (token.id == TokenId.Identifier)
{
anonymous = false;
StringBuilder full_type_name = new StringBuilder(GetIdentifier());
NextToken();
while (token.id == TokenId.Dot)
{
NextToken();
ValidateToken(TokenId.Identifier, Res.IdentifierExpected);
full_type_name.Append(".");
full_type_name.Append(GetIdentifier());
NextToken();
}
class_type = Type.GetType(full_type_name.ToString(), false);
if (class_type == null)
throw ParseError(Res.TypeNotFound, full_type_name.ToString());
}
ValidateToken(TokenId.OpenParen, Res.OpenParenExpected);
NextToken();
List<DynamicProperty> properties = new List<DynamicProperty>();
List<Expression> expressions = new List<Expression>();
while (true) {
int exprPos = token.pos;
Expression expr = ParseExpression();
string propName;
if (TokenIdentifierIs("as")) {
NextToken();
propName = GetIdentifier();
NextToken();
}
else {
MemberExpression me = expr as MemberExpression;
if (me == null) throw ParseError(exprPos, Res.MissingAsClause);
propName = me.Member.Name;
}
expressions.Add(expr);
properties.Add(new DynamicProperty(propName, expr.Type));
if (token.id != TokenId.Comma) break;
NextToken();
}
ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected);
NextToken();
Type type = anonymous ? DynamicExpression.CreateClass(properties) : class_type;
MemberBinding[] bindings = new MemberBinding[properties.Count];
for (int i = 0; i < bindings.Length; i++)
bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]);
return Expression.MemberInit(Expression.New(type), bindings);
}
Then, find the class Res and add the following error message:
public const string TypeNotFound = "Type {0} not found";
Et voilĂ , you will be able to construct queries like:
var carsPartial = cars.Select("new(name, year, (new your_namespace.maker(make.name as name)) as make)").ToList();
Make sure, you include the full type name including the whole namespace+class path.
To explain my change, it just checks if there is some identifier between new and opening parenthesis (see the added "if" at the begging). If so we parse full dot-separated class name and try to get its Type through Type.GetType instead of constructing own class in case of anonymous news.
If I've understood you correctly you want to make a plain anonymous class that contains fields from both class car and class maker. If it's the case you can just provide new names in that class, something like the following:
var carsPartial = cars.Select(c => new { year = c.year, name = c.name, make_name = c.make.name });
Or even provide names only to conflicting fields:
var carsPartial = cars.Select(c => new { c.year, c.name, make_name = c.make.name });

Categories

Resources