I am a beginner, and while testing some code, I can't seem to understand how to do this properly..
1st: I have a City class:
public class City
{
public City()
{
ZipCode = "";
Name = "";
}
public int Id { get; set; }
public string ZipCode { get; set; }
public string Name { get; set; }
}
2nd: I have a Contact class that uses a nullable City class (in case the user does not know the city):
public class Contact
{
public Contact()
{
Name = "";
Line1 = "";
Line2 = "";
CityId = null;
City = new City();
}
public int Id { get; set; }
public string Name { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
private int? _CityId;
public int? CityId
{
get { return _CityId; }
set { _CityId = value < 1 ? null : value; }
}
private City _City;
public City City
{
get { return _City; }
set { _City = _CityId == null ? null : value; }
}
}
The problem I encounter is when I retrieve a record that has stored a null City (when retrieving a record and its City is not null, everything will work fine). My .Select() statement looks like this:
var result = await _context.Contact
.Where(w => w.Id == id)
.Include(q => q.City)
.Select(s => new Contact
{
// Entity
Id = s.Id,
// Model
Line1 = s.Line1,
Line2 = s.Line2,
CityId = s.CityId,
City = new City // I am retrieving this so that I can get the inner data as well
{
// Entity
Id = s.City.Id,
// Model
ZipCode = s.City.ZipCode,
Name = s.City.Name,
}
}).FirstOrDefaultAsync();
The output is fine for records that does not have a null City, but if user retrieves a record with null City, it throws the following error:
Nullable object must have a value.
Can anybody please teach me how to do this properly? Thank you in advance!
You don't need to create a new Entity using Select, you are getting the error because if s.City is null s.City.Id doesn't exists. Insteat search it directly using
var result = await _context.Contact
.Include(q => q.City)
.FirstOrDefaultAsync(x => x.Id == id);
Why you use Select and using private property for the city?
Contact Class :
public class Contact
{
public Contact()
{
Name = "";
Line1 = "";
Line2 = "";
CityId = null;
City = new City();
}
public int Id { get; set; }
public string Name { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public int? CityId;
public City City
}
Your Select is the same as the entity class, and you don't need to use it.
var result = await _context.Contact
.Include(q => q.City)
.FirstOrDefaultAsync(f => f.Id == id);
Thanks for the answers everyone!
I found that the culprit is this line of code (thanks to your answers and explanations):
Id = s.City.Id
That is because Id here is an int (not nullable since this is an Identity in MSSQL) but when CityId (and also City object) stores a null in the database, s.City.Id will also contain null in the .Select().
The Id = s.City.Id in the above case fails because Id (not nullable) is being forced to receive a null (contained in the s.City.Id).
Related
I have some additional work to do get the value from a string and set to a property As shown in the example.
Here I am putting all the logic to extract a value from a string in a constructor and I have set all the property.
public class CourseModuleView
{
public CourseModuleView(string str)
{
var re = new Regex(#"'(.*?)'");
var results = Regex.Matches(str, #"'(.*?)'", RegexOptions.Singleline)
.Cast<Match>()
.Select(x => x.Groups[1].Value);
UserId = int.Parse(results.ElementAt(0));
ComponentType = results.ElementAt(1);
ModuleId = int.Parse(results.ElementAt(2));
}
public int Id { get; set; }
public int UserId { get; set; }
public int ModuleId { get; set; }
public string ComponentType { get; set; }
}
I am calling my constructor as:
//str will have the pattern like this,
//"The user with id '2209' viewed the 'url' activity with course module id '317'
// but it will be a list of string
var modCompletedResult = allLog
.Where(x => x.EventName == EventNameMessage.CourseModuleView)
.Select(x => new CourseModuleView(x.str)
{
//other property setting code goes here
}).ToList();
Sometimes if I want to inject other value in the same constructor (from the different methods) I have to pass both the unrelated value also.
What is the other most appropriate way to set the property while extracting the value from the string without writing all the logic in the constructor?
public int Id { get; set { this.Id = extractId(value); }}
public int UserId { get; set { this.UserId = extractUserId(value); }}
.....
Then assign it in the select:
var theString = "The user with id '2209' viewed the 'url' activity with course module id '317'.";
var modCompletedResult = allLog
.Where(x => x.EventName == EventNameMessage.CourseModuleView)
.Select(x => new CourseModuleView()
{
Id = theString,
UserId = theString,
......
}).ToList();
Sorry im using a phone, quite hard to write code here.
The C# setter and getter is powerful, you can even set other properties when assignning only one property. Example
public int Id { get;
set {
var extracted = extractAll(value);
this.Id = extracted.Id;
this.UserId = extracted.UserId;
.......
}
}
.Select(x => new CourseModuleView()
{
Id = theString, //assign only Id will automatically assign all properties according to the Id setter logic
}).ToList();
Btw you have to keep the name value in the setter, if not, it will try to find the named variable inside the local scope.
Sorry my mistake, when you assigning property in select, it has to be the same type, Id should be assigned by int.
For your case, you can make a dummy property of type string then apply the setter logic in it.
Example:
public string dummy { get; set { ..... }}
Hope this works for you.
public void SetEverything(string str)
{
var re = new Regex(#"'(.*?)'");
var results = Regex.Matches(str, #"'(.*?)'", RegexOptions.Singleline)
.Cast<Match>()
.Select(x => x.Groups[1].Value);
this.UserId = int.Parse(results.ElementAt(0));
this.ComponentType = results.ElementAt(1);
this.ModuleId = int.Parse(results.ElementAt(2));
}
public int Id { get; set; }
public int UserId { get; set; }
public int ModuleId { get; set; }
public string ComponentType { get; set; }
public string Setter { get { return ""; } //return empty string
set {
SetEverything(value);
}
}
Then
var modCompletedResult = allLog
.Where(x => x.EventName == EventNameMessage.CourseModuleView)
.Select(x => new CourseModuleView()
{
Setter = x.str,
}).ToList();
Using RavenDB v4.2 or higher, I want to setup an index that queries another collection. Basically, reproduce a WHERE IN clause in the mapping part of the index.
The models below represent two collections. Here each User has a collection of Device ID's:
class Device {
public string Id { get; set; }
public string Name { get; set; }
}
class User {
public string Id { get; set; }
public string BlogPostId { get; set; }
public List<string> DeviceIds { get; set; }
}
Now consider the following index as an example on what I'm trying to achieve:
public class DeviceIndex : AbstractIndexCreationTask<Device, DeviceIndex.Result>
{
public class Result
{
public string Id { get; set; }
public string DeviceName { get; set; }
public bool HasUser { get; set; }
public int UserCount { get; set; }
}
public DeviceIndex()
{
Map = devices => from d in devices
select new Result
{
Id = d.Id,
DeviceName = d.Name,
HasUser = ... ?, // How to get this from Users collection?
UserCount = ... ? // same...
};
}
How do I fill the HasUser true/false and UserCount properties in this index? E.g. how can I query the 'User' collection here?
Please note that this example is seriously simplified for brevity. I'm not so much interested in workarounds, or changing the logic behind it.
As #Danielle mentioned you need to use a mutli-map-index and reduce the result.
Here is a working example
public class DeviceIndex : AbstractMultiMapIndexCreationTask<DeviceIndex.Result>
{
public class Result
{
public string Id { get; set; }
public string DeviceName { get; set; }
public bool HasUser { get; set; }
public int UserCount { get; set; }
}
public DeviceIndex()
{
AddMap<User>(users => from u in users
from deviceId in u.DeviceIds
let d = LoadDocument<Device>(deviceId)
select new Result
{
Id = d.Id,
HasUser = true,
UserCount = 1,
DeviceName = d.Name,
});
AddMap<Device>(devices => from d in devices
select new Result
{
Id = d.Id,
HasUser = false,
UserCount = 0,
DeviceName = d.Name,
});
Reduce = results => from result in results
group result by new { result.Id } into g
select new Result
{
Id = g.First().Id,
DeviceName = g.First().DeviceName,
HasUser = g.Any(e => e.HasUser),
UserCount = g.Sum(e => e.UserCount),
};
}
}
and you can call it like this
var result = await _session.Query<DeviceIndex.Result, DeviceIndex>().ToListAsync();
If you would have a Users List in the Device class List<string> Users
a list that contains the document ids from the Users collection then you could Index these Related documents.
See:
https://demo.ravendb.net/demos/csharp/related-documents/index-related-documents
Or do the opposite,
Create an index on the Users collection, and index the related Device info
Without changing current models,
You can create a Multi-Map Index to index data from different collections.
https://ravendb.net/docs/article-page/4.2/csharp/indexes/multi-map-indexes
https://ravendb.net/docs/article-page/4.2/csharp/studio/database/indexes/create-multi-map-index
https://ravendb.net/learn/inside-ravendb-book/reader/4.0/10-static-indexes-and-other-advanced-options#querying-many-sources-at-once-with-multimap-indexes
Multiple answers have led me to the following 2 solutions, but both of them do not seem to be working correctly.
What I have are 2 objects
public class DatabaseAssignment : AuditableEntity
{
public Guid Id { get; set; }
public string User_Id { get; set; }
public Guid Database_Id { get; set; }
}
public class Database : AuditableEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Server { get; set; }
public bool IsActive { get; set; }
public Guid DatabaseClientId { get; set; }
}
Now, the front-end will return all selected Database objects (as IEnumerable) for a given user. I am grabbing all current DatabaseAssignments from the database for the given user and comparing them to the databases by the Database.ID property. My goal is to find the DatabaseAssignments that I can remove from the database. However, my solutions keep returning all DatabaseAssignments to be removed.
if (databases != null)
{
var unitOfWork = new UnitOfWork(_context);
var userDatabaseAssignments = unitOfWork.DatabaseAssignments.GetAll().Where(d => d.User_Id == user.Id);
//var assignmentsToRemove = userDatabaseAssignments.Where(ud => databases.Any(d => d.Id != ud.Database_Id));
var assignmentsToRemove = userDatabaseAssignments.Select(ud => userDatabaseAssignments.FirstOrDefault()).Where(d1 => databases.All(d2 => d2.Id != d1.Database_Id));
var assignmentsToAdd = databases.Select(d => new DatabaseAssignment { User_Id = user.Id, Database_Id = d.Id }).Where(ar => assignmentsToRemove.All(a => a.Database_Id != ar.Database_Id));
if (assignmentsToRemove.Any())
{
unitOfWork.DatabaseAssignments.RemoveRange(assignmentsToRemove);
}
if (assignmentsToAdd.Any())
{
unitOfWork.DatabaseAssignments.AddRange(assignmentsToAdd);
}
unitOfWork.SaveChanges();
}
I think u are looking for an Except extension, have a look at this link
LINQ: Select where object does not contain items from list
Or other way is with contains see below Fiddler link :
https://dotnetfiddle.net/lKyI2F
Im trying to use Dapper in an ASP.Net Core application to map multiple tables to one object with other objects as its properties.
My tables are as follows (just basic summary):
user table
address table (no user ids stored in this table, this table is just address records)
address_type table (lookup table)
phone_number table (no user ids stored in this table, this table is
just phone records)
phone_number _type table (lookup table)
user_has_address table - this table has a user_id, address_id and address_type_id
user_has_phone_number table - this table has a
user_id, phone_number _id and phone_number _type_id
Basically whats happening is results get returned fine if all the users have no address or phone records or if only the first user in the list has address/phone records. What I want is if a user has an address/phone number then that dictionary is populated, otherwise I still want all the user info but that address/phone number dictionary will be empty.
My objects look like the following:
public class User
{
public uint id { get; set; }
public DateTime modified_date { get; set; }
public uint modified_by { get; set; }
public string user_name { get; set; }
public uint company_code { get; set; }
public string email { get; set; }
public bool active { get; set; }
public string first_name { get; set; }
public string last_name { get; set; }
public Dictionary<uint, Address.Address> addresses { get; set; }
public Dictionary<uint, Address.PhoneNumber> phone_numbers { get; set; }
}
public class Address
{
public uint address_id { get; set; }
public AddressType address_type { get; set; }
public string address_line1 { get; set; }
public string address_line2 { get; set; }
public string address_line3 { get; set; }
public string city { get; set; }
public string state { get; set; }
public string country_code { get; set; }
public string postal_code { get; set; }
public sbyte is_po_box { get; set; }
}
public class AddressType
{
public uint id { get; set; }
public string name { get; set; }
}
public class PhoneNumber
{
public uint id { get; set; }
public PhoneNumberType phone_number_type { get; set; }
public string phone_number { get; set; }
public string phone_ext { get; set; }
}
public class PhoneNumberType
{
public uint id { get; set; }
public string name { get; set; }
}
Here is my function where I try to use Dapper to map to the User class:
public List<User> GetUsersByStatus(uint companyCode, string status)
{
if (companyCode == 0)
throw new ArgumentOutOfRangeException("companyID", "The Company ID cannot be 0.");
List<User> Users = new List<User>();
try
{
string sql = #"SELECT u.*, ad.*, adt.*, p.*, pt.*
FROM master.user u
LEFT JOIN master.user_has_address AS uha ON uha.user_id = u.id
LEFT JOIN master.address AS ad ON ad.id = uha.address_id
LEFT JOIN master.lookup_address_type adt ON adt.id = uha.address_type_id
LEFT JOIN master.user_has_phone_number AS uhp ON uhp.user_id = u.id
LEFT JOIN master.phone_number AS p ON p.id = uhp.phone_number_id
LEFT JOIN master.lookup_phone_number_type pt ON pt.id = uhp.phone_number_type_id
WHERE u.company_code = " + companyCode;
switch (status)
{
case "1":
// Active Status.
sql = sql + " AND (u.active = TRUE)";
break;
case "2":
// Retired Status.
sql = sql + " AND (u.active = FALSE)";
break;
}
sql = sql + " ORDER BY u.user_name";
using (var conn = new MySqlConnection(connectionString))
{
conn.Open();
var userDictionary = new Dictionary<uint, User>();
conn.Query<User, Address, AddressType, PhoneNumber, PhoneNumberType, User>(sql, (u, ad, adt, p, pt) =>
{
User user;
if (!userDictionary.TryGetValue(u.id, out user))
userDictionary.Add(u.id, user = u);
if (ad != null && adt != null)
{
Address address = ad;
address.address_type = new AddressType() { id = adt.id, name = adt.name };
if (user.addresses == null)
user.addresses = new Dictionary<uint, Address>();
if (!user.addresses.ContainsKey(adt.id))
user.addresses.Add(adt.id, address);
}
if (p != null && pt != null)
{
PhoneNumber phone = p;
phone.phone_number_type = new PhoneNumberType() { id = pt.id, name = pt.name };
if (user.phone_numbers == null)
user.phone_numbers = new Dictionary<uint, PhoneNumber>();
if (!user.phone_numbers.ContainsKey(pt.id))
user.phone_numbers.Add(pt.id, phone);
}
return user;
},
splitOn: "id,id,id,id").AsQueryable();
Users = userDictionary.Values.ToList();
}
}
catch(Exception ex)
{
//TO DO: log exception
}
return Users;
}
I've tried tracing through it and in cases where the 3rd user (for example) has address/phone records, it seems to grab the first 2 users fine but then jump right out of the query portion when it gets to the record with address/phone numbers and then it returns an empty Users list.
Does anyone have any idea what Im doing wrong?
I'm not sure this is the solution, but I have a problem with this code:
User user;
if (!userDictionary.TryGetValue(u.id, out user))
userDictionary.Add(u.id, user = u);
Remember that u is parameter of the lambda, and will be changed during execution. I would do this:
User user;
if (!userDictionary.TryGetValue(u.id, out user))
{
user = new User(u); // Make a new instance of user
userDictionary.Add(u.id, user);
}
Also you should definitely use parameters for your query:
WHERE u.company_code = #CompanyCode";
And finally I don't think it should be the responsibility of this code to construct dictionaries for holding addresses and phone numbers. The User constructor should take care of that.
Unfortunately, I must have 50 rep to add a comment, so I'll make this an answer.
Are all the fields in the DB concerning phone and address non-nullable? If no, then the mistake could be that it can't match them with your C# classes. In this case, declare immutable types nullable so that they match DB types. Like this:
int? val;
Turns out the main issue was the fact that I left the address_id parameter in the Address class named the way it was, it should ahve been just 'id'. I updated that and it works now, I did also update my code according to Palle Due's suggestion so that may have contributed as well.
I am trying query all countries and in each country object it would fill up the provinces.
I have the following Classes
public class Country
{
public int Countryid { get; set; }
public string CountryName { get; set; }
public IEnumerable<Province> Provinces { get; set; }
}
public class Province
{
public int ProvinceId { get; set; }
public string ProvinceName { get; set; }
}
public IEnumerable<Country> GetCountries()
{
var query = #"
SELECT [Country].[CountryId], [Country].[Name] as CountryName, [Province].[ProvinceId], [Province].[Name] as ProvinceName
FROM [Province]
RIGHT OUTER JOIN [Country] ON [Province].[CountryId] = [Country].[CountryId]
WHERE [Country].[CountryId] > 0";
return _connection.Query<Country, Province, Country>(query, (country, province) =>
{
country.Provinces = country.Provinces.Concat(new List<Province> { province });
return country;
}, null);
}
The error that I get is the following:
System.ArgumentException: 'When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id
Parameter name: splitOn'
I have been following this example:
https://gist.github.com/Lobstrosity/1133111
From my perspective, I don't see what I did much different besides mine being an outer join which I think should not matter, the result format is about the same.
Why do I need the split on and can this work without it?
IIRC, I did something like this.
var query = #"
SELECT [Country].[CountryId], [Country].[Name] as CountryName, [Province].[ProvinceId], [Province].[Name] as ProvinceName
FROM [Province]
RIGHT OUTER JOIN [Country] ON [Province].[CountryId] = [Country].[CountryId]
WHERE [Country].[CountryId] > 0";
List<Country> countries = new List<Country>();
_connection.Query<Country, Province, Country>(query, (country, province) =>
{
Country lastCountry = countries.FirstOrDefault(d => d.CountryId == country.Id);
if(lastCountry == null)
{
countries.Add(country);
lastCountry = country;
}
lastCountry.Provinces = lastCountry.Provinces.Concat(new List<Province> { province });
return lastCountry;
}, null);
return countries;
I typed this out in LinqPad, so you will need to debug and check it is correct, been a long time since I used Dapper in anger