dapper left join & mapping query - .net-core

There is one-to-many relation between Brand and Campaign entities,
With given Id I need to select Campaign and related Brand entity along with it.
TO do so:
public async Task<Campaign> GetAsync(int id)
{
using var dbConnection = _context.CreateConnection();
string query = #"SELECT c.[Id], c.[BrandId], c.[StartDate],
c.[EndDate],b.[Id] FROM [dbo].[Campaign] c
left join [dbo].[Brand] b on c.[BrandId] = b.[Id]
WHERE c.[Id] = #Id";
var campaign = await dbConnection.QueryAsync<Campaign, Brand, Campaign>(query, (campaign, brand) =>
{
campaign.Brand = brand;
return campaign;
}, splitOn: "BrandId", param: new { Id = id });
return campaign.FirstOrDefault();
}
code above not throws exception but child brand entity is not correct.(its dummy record, and BrandId is 0 whereas its valu is 5 in database)
whats missing here?
entity def:
public class Campaign : SqlEntityBase
{
public int BrandId { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public Brand Brand { get; set; }
}
public class Brand : SqlEntityBase
{
public string Name { get; set; }
public List<Campaign> Campaign { get; set; } = new List<Campaign>();
}

Your SQL query doesn't make sense. You're splitting on BrandId in dapper but you never select anything to do with the brand in the query. With your current code, dapper is parsing the SQL column Id into your Campaign POCO (which it cant do because there isn't a property called Id nor have you anything mapped to it in the campaign class).
And then it see's BrandId and then parses everything after that into the Brand POCO, but your remaining columns that you're selecting are the start and end dates for the campaign.
In summary: You need to include the Brand data into the SQL query. You're joining onto the table, but only selecting the campaign data.

Related

Dynamically paging EF Core results

I have a UI grid which permits sorting by column :
Id
Organisation Name
Organisation type
Departments
1
first
some type
def
2
second
another type
abc, def
2
third
some type
xyz
See the entities below:
public class Organisation
{
public int Code { get; set; }
public string Type { get; set; }
public string Name { get; set; }
public List<Department> Departments { get; set; }
}
public class Department
{
public int Code { get; set; }
public string Name { get; set; }
}
I want to be able to sort the table values by Departments which is a comma separated values that comes from Organization.Departments.Select(p=> p.Name);
I would like to make the sorting as an IQueryable and avoid bringing all the data in memory because After sorting I will apply the pagination and I don't want to bring all the DB records in memory.
I'm using the following extension method for sorting, but it is not working for nested collections:
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string sortProperty, ListSortDirection sortOrder)
{
var type = typeof(T);
var property = type.GetProperty(sortProperty, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (property == null)
throw new OperationFailedException($"Sorting by {sortProperty}");
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExp = Expression.Lambda(propertyAccess, parameter);
var typeArguments = new Type[] { type, property.PropertyType };
var methodName = sortOrder == ListSortDirection.Ascending ? "OrderBy" : "OrderByDescending";
var resultExp = Expression.Call(typeof(Queryable), methodName, typeArguments, source.Expression, Expression.Quote(orderByExp));
return source.Provider.CreateQuery<T>(resultExp);
}
This method works fine for properties that are at object level.
IQueryable I'm using later for sorting looks something like this:
var iQueryableToBeSorted = _dbContext.Organization.Include(p=>p.Departments).AsQueryable();

Elasticsearch id field is empty using Nest IndexMany with Bulk indexing

Somehow, my object's id field is not being stored, but all other fields are?
The code:
// Assuming a list of Products
var products = // entity framework code to get a list of products
var node = new Uri("http://localhost:9200");
var settings = new ConnectionSettings(node);
var client = new ElasticClient(settings);
var descriptor = new BulkDescriptor();
descriptor.IndexMany(products, (bd, q) => bd.Id(q.id).Index("products"));
client.Bulk(descriptor);
The sample product class I'm using:
public class Product
{
public string id { get; set; }
public string nameEn { get; set; }
public string nameFr { get; set; }
}
Result as shown in Kabana:
id: nameEn: nameFr: _id:C _type:productsearch _index:products _score:1
Note how the id field is empty, but the Elasticsearch _id field is defined correctly.
Thanks for any help!

Can I do an UPDATE on a JOIN query with OrmLite on ServiceStack?

I want to do an update for a specific field on a table based on the results from a query that includes a join. Using OrmLite with ServiceStack.
My Classes are as follows:
public class Document
{
public int Id { get; set; }
public string BCL_Code { get; set; }
public bool IsActive { get; set; }
public int DocumentTypeId { get; set; }
}
public class DocumentType
{
public int Id { get; set; }
public string TypeName { get; set; }
}
Trying to do the Update on the Join with the following code:
var q = db.From<Document>()
.Join<Document, DocumentType>(
(doc, type) => doc.DocumentTypeId == type.Id)
.Where(d => d.BCL_Code == "FR49")
.And<DocumentType>(t => t.TypeName == "Enrollment Process");
db.UpdateOnly(new Document { IsActive = false }, onlyFields: q);
I know I can update specific fields, and I know how to do joins, but when I try to include a join in the query, and then do an UpdateOnly, I get an error message on the db.UpdateOnly line:
The multi-part identifier "DocumentType.TypeName" could not be bound.
Is it possible to do an Update on a Join Query?
If so, what is the proper way to do it?
There's no Typed APIs for Update From Table in OrmLite yet, you can add a feature request for it.
In the meantime you can use Custom SQL, e.g:
db.ExecuteSql(#"UPDATE Document SET IsActive = #isActive
FROM Document d
INNER JOIN DocumentType t ON (d.DocumentTypeId = t.Id)
WHERE d.BCL_Code = 'FR49'
AND t.TypeName = 'Enrollment Process'",
new { isActive = false });

EF relation in constructor

I get error;
Cannot implicitly convert type Sale in ICollection
when I do this, I have seen it in some examples and it is possible to do so.
Sale sale= new Sale(){ Id = 1 };
Service service = new Service() { Name= "SomeService",Sales=sale}; //I can't assign sale
public class Service: EntityBase
{
//public Service()
//{
// Sales= new HashSet<Sale>();
//}
public string Name{ get; set; }
public virtual ICollection<Sale> Sales{ get; set; }
}
How can i solve it?
I don't want to do: service.Sales.add(fact), if there is another way
If you were very opposed to using .Add and wanted to pass one or more Sale instances to the Service instance and have them added to the Sales collection then you could add a second constructor that takes one or more Sale instances like so.
public class Service: EntityBase
{
public Service() // empty constructor needed for EF
{}
public Service(params Sale[] sales)
{
Sales = new List<Sale>(sales);
}
public string Name{ get; set; }
public virtual ICollection<Sale> Sales{ get; set; }
}
// example with 2 sales
Sale sale1 = new Sale(){ Id = 1 };
Sale sale2 = new Sale(){ Id = 2 };
Service service = new Service(sale1,sale2) { Name= "SomeService" };
Leave the empty constructor in place as it is used by EF.
What you're saying here is that a Sale = List();
make a list of sales, add a sale to it, and then in the constructor say that Sales = the list of sales u made.
Sale sale = new Sale(){ Id = 1 };
List<Sale> sales = new List<Sale>();
sales.Add(sale);
Service service = new Service() { Name = "Service", Sales = sales };

Entity Framework : adding record with related data

I have a very simple Situation with 2 tables
public class Movie
{
[Key]
public Guid ID { get; set; }
public string Name { get; set; }
public byte[] Hash { get; set; }
public int GenreID{ get; set; }
[ForeignKey("GenreID")]
public virtual Genre genre{ get; set; }
}
and
public class Genre
{
public int ID { get; set; }
public string Name { get; set; }
}
Now, in an import sequence I want to create new movies and link the Genre with the existing entries in the Genre table or create new Genre entries if they don't exist.
Movie m = new Movie();
m.ID = Guid.NewGuid();
IndexerContext db = new IndexerContext();
var genre = db.Genre.Where(g => g.Name== genreValue).FirstOrDefault();
if(genre!= null)
{
m.GenreID= genre.GenreID;
}
else
{
genre= new Genre();
genre.Name = genreValue;
db.Genres.Add(genre);
var genreCreated= db.Genre.Where(g => g.Name== genreValue).FirstOrDefault();
m.GenreID= genreCreated.GenreID;
}
Now the problem is, it doesn't work. The last line fails because genreCreated is null.
Plus I think I must doing it wrong - it can't be that difficult in Entity Framework.
can anyone help me?
db.Genres.Add(genre);
This does not send insert statement to database - this instructs entity framework that new record should be inserted when saving changes. Genre will be saved (and created id available) after you call db.SaveChanges(); As for now, you do not have save call, so genreCreated is null.
In your situation - fix is simple, you do not need to select genreCreated from db. Just setting m.Genre to new value should do the job
Movie m = new Movie();
m.ID = Guid.NewGuid();
IndexerContext db = new IndexerContext();
var genre = db.Genre.Where(g => g.Name== genreValue).FirstOrDefault();
if(genre! = null)
{
m.GenreID = genre.GenreID;
}
else
{
genre = new Genre();
genre.Name = genreValue;
m.Genre = genre;
}
db.SaveChanges(); //m.GenreID will automatically be set to newly inserted genre
After the add statement you need to save it:
Try
genre= new Genre();
genre.Name = genreValue;
db.Genres.Add(genre);
db.SaveChanges();

Resources