a question about EntityFramework and SQLite.
When I work with SQL Server and I want to do a query over a table I would access the required table (for example "Messages") very easily like so:
_databaseDBContext.Messages.where(m => m.id == id);
But on SQLite I can not access the table by simple entering the name, instead I have use the Set<>() method like so:
_SqliteDBContext.Set<Message>().where(m => m.id == id);
It got me thinking what would happen if I had two tables like:
DbSet<Message> Messages { get; set; }
DbSet<Message> OtheTypes { get; set; }
How can I do to properly indicate each other?
Thanks!
The function .Set<T>() has an overload method that takes a table name; .Set<Message>(string name). So you can use that to differentiate between the two tables.
Where clause example:
var messages = _SqliteDBContext.Set<Message>("Messages").Where(x => x.id == 2);
var messages = _SqliteDBContext.Set<Message>("OtheTypes").Where(x => x.id == 2);
Example below using C# Query Language and the two database tables:
var query = from message in _SqliteDBContext.Set<Message>("Messages")
join otherType in _SqliteDBContext.Set<Message>("OtheTypes") on messages.id equals otherType.id
select new { message, otherType};
EF Core only supports having one mapping in the model for each entity type. I.e. there aren't any supported cases in which two instance of the same type could end up being stored in different tables or different columns.
But you can do a simple trick:
public class Messagetwo : Message
{
}
And then in context
DbSet<Message> Messages { get; set; }
DbSet<Messagetwo> OtheTypes { get; set; }
Related
I have a DbSet class:
public class Manufacturer
{
public Guid Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
public virtual Category Category { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
I know I can use Skip() and Take() to get limited manufacturers. But my requirement is to get limited Products of all the manufacturers. I'm using something like this but it's not working
var manufacturers = await _context.Manufacturers.Where(x => x.Products.Take(10))
.ToListAsync();
PS: I'm using Lazy Loading (Not eager loading)
Compile error is:
Cannot implicitly convert type
'System.Collections.Generic.IEnumerable<Domain.Product>' to 'bool'
Cannot convert lambda expression to intended delegate type because
some of the return types in the block are not implicitly convertible
to the delegate return type
How can I achieve to get all the manufacturers but limited products in them?
I believe there is no way to do this directly with a queryable source. You can manage it in memory.
var manufacturers = await _context.Manufacturers.Include(m => m.Products).ToListAsync();
foreach(var m in manufacturers)
{
m.Products = m.Products.Take(10).ToList();
}
This will get all products for each manufacturer from the DB and then keep only the first 10.
You can load the Manufacturer entity without the Product list first (so without an Include() call) and then run a separate query to load only the products you want for a specific Manufacturer entity. EF will automatically update the navigation properties. See the following example (authors can have multiple posts in this example):
using (var context = new MyContext())
{
Author author = context.Author.First();
Console.WriteLine(context.Post.Where(it => it.Author == author).Count());
context.Post.Where(it => it.Author == author).Take(2).ToList();
Console.WriteLine(author.Posts.Count());
}
This will generate the following output:
3
2
Even though there are three entries available in my test database, only two are actually read. See the generated SQL queries:
For the Author author = context.Author.First(); line:
SELECT `a`.`Id`, `a`.`Name`
FROM `Author` AS `a`
LIMIT 1
For the context.Post.Where(it => it.Author == author).Count() line:
SELECT COUNT(*)
FROM `Post` AS `p`
INNER JOIN `Author` AS `a` ON `p`.`AuthorId` = `a`.`Id`
WHERE `a`.`Id` = 1
For the context.Post.Where(it => it.Author == author).Take(2).ToList(); line:
SELECT `p`.`Id`, `p`.`AuthorId`, `p`.`Content`
FROM `Post` AS `p`
INNER JOIN `Author` AS `a` ON `p`.`AuthorId` = `a`.`Id`
WHERE `a`.`Id` = 1
LIMIT 2
However, you have to do this trick for each individual Manufacturer entity, that it loads only ten associated Product entities. This can result in 1+N SELECT queries.
Try the longer way:
_await _context.Manufacturers.Select(x =>
{
x.Products = x.Products.Take(10).ToList();
return x;
}).ToListAsync();
I have an application where I can choose a database with the help of a combobox and then apply several query operations on this database within application. For this purpose, I have generated 6 different .edmx files which is generated from my databases with the database first approach. Besides that, I want to create a Data Access Layer class where I am managing my data access operations, but I want this class to be generic so that I am not going to need to create 6 different data access classes for each entity. For this purpose, I created such a class;
public class UserDetails<TEntity> where TEntity:class
{
private DbContext _context { get; set; }
private DbSet<TEntity> efDB { get; set; }
public UserDetails(DbContext context)
{
_context = context;
efDB = _context.Set<TEntity>();
}
public List<UserDetailsModel> GetUserDetails(int? roleID, int? titleID, int? managerID, string id = "", string name = "", string surname = "", string mail = "")
{
using (efDB) {
List<UserDetailsModel> query = (from user in efDB.TABLE_USER
join pass in efDB.TABLE_PASSWORD_HISTORY on GetLatestPassword(user.ID) equals pass.ID into passJoined
from passLatest in passJoined.DefaultIfEmpty()
join role in efDB.TABLE_REL_USER_ROLE on user.ID equals role.UserID into roleJoined
from roleRel in roleJoined.DefaultIfEmpty()
join defRole in efDB.TABLE_DEF_ROLE on roleRel.RoleID equals defRole.ID into defRoleJoined
from defRoleRel in defRoleJoined.DefaultIfEmpty()
join title in efDB.TABLE_USER_TITLES on user.TitleID equals title.ID
where user.Name.Contains(name) && user.Surname.Contains(surname) && user.Email.Contains(mail)
select new UserDetailsModel() {
ID = user.ID,
Name = user.Name,
Surname = user.Surname,
Email = user.Email,
TitleID = (int)user.TitleID,
TitleName = title.Description,
Password = passLatest.Password,
RoleID = roleRel.RoleID,
RoleName = defRoleRel.RoleName,
}
).ToList();
return query;
}
As you see, I want to perform a linq query in my GetUserDetails method. Note that, all of the 6 databases have same tables with the same names so that this query is going to work for all of my entities. What I want to do here is that, use the variable efDBgenerically so that it can be any of the 6 database entites. The desired database entity instance will be created by a different class which works like a factory and will be given to my UserDetails data access class. But obvisiouly, my code won't work. How can I implement this generic logic into my system? Thanks in advance.
I think you want something like UnitOfWork. Here are a few links describing and explaining how to set one up.
https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
https://www.codeproject.com/Articles/770156/Understanding-Repository-and-Unit-of-Work-Pattern
https://www.programmingwithwolfgang.com/repository-and-unit-of-work-pattern/
I am using Entity Framework Core 2.2.6. I'm going to try and make this question concise and apologies in advance if it ends up being a wall of text.
The error I am seeing is an ambiguous column name in the SQL Entity Framework Core generates.
So my situation is this: I have two entities with a many-to-one relationship. The "parent" entity implements
an interface that has a property that is of type IChildEntity. Here are the interfaces:
public interface IParentEntity
{
IChildEntity Child { get; set; }
string Prop1 { get; set; }
string Prop2 { get; set; }
}
public interface IChildEntity
{
string ChildProp1 { get; set; }
string ChildProp2 { get; set; }
}
I am using ef core's fluent api and in order to set up the relationship between parent and child
I am using a concrete type of ChildEntity and defining a IChildEntity property to conform to the
interface and just passing things through to the concrete type:
public class ChildEntity : IChildEntity
{
public long ID {get; set;}
public string ChildProp1 { get; set; }
public string ChildProp2 { get; set; }
}
public class ParentEntity : IParentEntity
{
public long ID { get; set; }
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public long ChildID { get; set; }
// Navigation property so EF Core can create the relationship
public ChildEntity MappedChild { get; private set; }
// this is to adhere to the interface
// just pass this through to the backing concrete instance
[NotMapped]
public IChildEntity Child
{
get => MappedChild;
set => MappedChild = (ChildEntity)value;
}
}
Then in OnModelCreating I set up the relationship like so:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ParentEntity>()
.HasOne(e => e.MappedChild)
.WithMany()
.HasForeignKey(e => e.ChildID);
}
This works and the relationship gets set up as expected, however I am finding when I do a query it can generate
some SQL that can result in an ambigous column error in some database engines. Here is the example query:
MyContext.ParentEntity
.Include(p => p.MappedChild)
.Where(p => p.Prop1.Equals("somestring")
.FirstOrDefault()
The SQL that gets generated is similar to:
SELECT p."ID", p."ChildID", p."Prop1", p."Prop1", "p.MappedChild"."ID", "p.MappedChild"."ChildProp1", "p.MappedChild"."ChildProp2"
FROM "ParentEntity" AS p
INNER JOIN "ChildEntity" AS "p.MappedChild" ON p."ChildID" = "p.MappedChild"."ID"
WHERE p."Prop1" = 'somestring'
ORDER BY "p.MappedChild"."ID"
LIMIT 1
The problem here is we are selecting two columns with the name ID and not aliasing. Some databases will be ok with this
but some will not. A work around I can do for this is to do two separate queries to get the entity and the child entity:
var parent = MyContext.ParentEntity
.Where(p => p.Prop1.Equals("somestring")
.FirstOrDefault()
MyContext.Entry(parent).Reference(p => s.MappedChild).Load();
But this is less than ideal since it does multiple queries and is a bit less elegant than just using Include()
Because this seems like such a common use case and I couldn't find any bug reports against EF Core for this type of
behavior it is my suspicion that I am doing something wrong here that is resulting in EFCore not aliasing column names
for this type of query. I was thinking it could be the bit of trickery I have to do to ensure my entity implements it's interface
(this is something I can't due to constraints in the codebase and other integrations) but the more I look at it the less likely that
seems to me since we are directly dealing with the "mapped" property in EF related code and it's completely unaware of the interface.
My questions are - can anyone see something in my implementation that would cause this? Could anyone
suggest a better workaround than what I have here? Any advice here would be appreciated. Thanks much.
This is an old Entity framework bug with the Oracle company products bug including the MySQL database and Oracle database (12.1 and older).
I see the
ORA-00918: column ambiguously defined
error mostly when:
Selecting one entity with including parent entity.
Selecting one entity with value object own one command
This error appears when using Find, First, FirstOrDefault, Last, Single and all single entity selector commands.
I tested many solutions and check generated sql statement to find out a very unique way without any performance overhead:
// This the way of getting one entity from oracle 12.1 without throwing Oracle exception => ORA-00918: column ambiguously defined without any extra overhead
var entities = await dbSet.Where(x => x.Id == id).Take(1).ToListAsync();
var entity = entities.FirstOrDefault();
Another Sample:
var entities = await dbSet.OrderByDescending(x => x.Id).Take(1).ToListAsync();
var entity = entities.FirstOrDefault();
At the end of your IQueryable Linq add Take(1) and get all with .ToList() or .ToListAsync() to execute the statement and fetch a list with one record. Then use Enumerable Single Entity Selector to change the list to an entity.
That’s all.
I'm trying to perform a simple query and the result data is almost all null.
I have this table structure
Table Registros
ID | Autonumeric
TareaM_Id | Numeric
Fecha | Date/Time
and Macro_tareas table
ID | Autonumeric
Nombre | Short Text
I have mapped the classes in C# like this:
[Table("Registros")]
public class Registro
{
[Column("ID")]
public virtual int ID { get; set; }
[Column("Fecha")]
public virtual DateTime Fecha { get; set; }
[Column("TareaM_Id")]
public virtual int TareaM_Id { get; set; }
public virtual MacroTarea MacroT { get; set; }
}
[Table("Macro_tarea")]
public class MacroTarea
{
[Column("ID")]
public virtual int ID { get; set; }
[Column("Nombre")]
public virtual string Nombre{ get; set; }
public virtual ICollection<Registro> Registros { get; set; }
}
This is the query i'm trying to use
string sql = #"SELECT reg.ID, mac.ID
FROM Registros as reg INNER JOIN Macro_tarea as mac on reg.TareaM_Id = mac.ID
WHERE Fecha = #Fecha";
using (IDbConnection db = new OleDbConnection(ConnectionString))
{
var result = db.Query<Registro,MacroTarea, Registro>(sql,
(reg,mac) =>
{
reg.MacroTarea = mac;
return reg;
}
,new { #Fecha = new DateTime(2019, 1, 4).Date }
, splitOn: "mac.ID")
.AsList();
}
I'm trying to only retrieve ids, but both id become null why is this happening?
The thing is, if I add Registros.Fecha and Macro_tarea.Nombre to the query, it got the value correctly. But id keep coming null.
Apparently the issue is happening only with ids. I suspect this issue is due to duplicate column names.
I'm working with Microsoft Access just in cast that matters.
My question is not similar to the possible duplicate because I have the classes defined as they should be mapped.
Renaming your database columns because your code cannot cope with the data is not a good idea. In the world of separation of concerns, why should your database care? There are good database reasons to name ID columns "Id", and you may not even have the option to change them.
There's another issue with Dapper mapping that renaming columns does not get around; repeated types. If you are trying to map to more than one instance of a class Dapper gets confused, and renaming columns won't work because you will rename both instances.
Here is the solution I have come up with. It's similar to a lot of examples that use a dictionary, except:
it can nest to as many levels as you like
can cope with Dappers 7 item limit
can cope with duplicates of the same class
can be reused e.g., for Get, GetCurrent and GetAll
In this example there is an Auction that has many Lots. Each Lot may have 1 or many Items. Items might be packs of Items. The Items are from a limited catalogue and we like relational data, so a Things table contains the details on each Item, like colour, size, etc. Here we are only getting a single Lot, but getting an Auction is the same with another level on top for Auction.
Parameter 1 - The SQL to get everything in one go
Parameter 2 - A Type array of each object we'll get back. For this reason it's best to order your SELECT to group the fields into the classes
Parameter 3 - Call the method we're about to write with the SQL result
Parameter 4 - Standard parameter array for the SQL. SQL Injection is bad, m'kay?
public async Task<List<Lot>> GetAll(int auctionId)
{
using (var connection = new SqlConnection(_appSettings.ConnectionString))
{
await connection.OpenAsync();
var result = new List<Lot>();
await connection.QueryAsync($#"
SELECT [Lot].*,
[Item].[Id],
[Item].[LotId],
[Item].[Notes],
itemDetails.[Id],
itemDetails.[ThingId],
itemDetails.[Colour],
itemDetails.[Size],
[SubItem].[Id],
[SubItem].[ItemId],
[SubItem].[Notes],
subItemDetails.[Id],
subItemDetails.[ThinId],
subItemDetails.[Colour],
subItemDetails.[Size]
FROM [Lot]
INNER JOIN [Item] ON [Item].[LotId] = [Lot].[Id]
LEFT JOIN [Thing] AS itemDetails ON itemDetails.[Id] = [Item].[ThingId]
LEFT JOIN [SubItem] ON [SubItem].[ItemId] = [Item].[Id]
LEFT JOIN [Thing] AS subItemDetails ON subItemDetails.[Id] = [SubItem].[ThingId]
WHERE [AuctionId] = #{nameof(auctionId)}
ORDER BY [Lot].[Id], [Item].[Id], [Expansion].[Id];",
new Type[] {
typeof(Lot),
typeof(Item),
typeof(Thing),
typeof(Expansion),
typeof(Thing)
},
MapResult(result),
new
{
AuctionId = auctionId
}
);
return result.ToList();
}
}
private Func<object[], Lot> MapResult(List<Lot> result)
{
return (obj) =>
{
Lot lot = (Lot)obj[0];
Item item = (Item)obj[1];
Thing itemDetails = (Thing)obj[2];
SubItem subItem = (SubItem)obj[3];
Thing subItemDetails = (Thing)obj[4];
if (lot != null)
{
if (result.Any(a => a.Id == lot.Id))
{
lot = result.First(a => a.Id == lot.Id);
}
else
{
result.Add(lot);
}
}
if (item != null)
{
if (lot.Items.Any(i => i.Id == item.Id))
{
item = lot.Items.First(i => i.Id == item.Id);
}
else
{
lot.Items.Add(item.FromThing(itemDetails));
}
}
if (expansion != null)
{
if (item.SubItems.Any(e => e.Id == subItem.Id) == false)
{
item.SubItems.Add(subItem.FromThing(subItemDetails));
}
}
return null;
};
}
MapResult is the meat of the code. It returns a Func with two types, the Type array we defined above and the return Type, and takes a List of the top level object.
I then map each item from the object array to another of it's actual type. This keeps the code easier to read, and enables properties and methods of the object to be accessed without issue.
Then it's a case of stepping down the hierarchy, checking at each step if one already exists with a matching id, and swapping the iterator to a reference to it if it does. This means that following code will add to the existing item.
In the particular case I've also added a FromThing function to allow easier combining of object properties.
As we discussed in comments, this is an issue due to duplicate column names in two tables. This is where the similar issue and solution could be found. But, it does not include "mapping by code" as you said. So it is not exact duplicate.
I suggest you change the names of ID fields in your tables to avoid colliding them. Of-course, you should also change the name of your POCO properties and mappings accordingly.
If you cannot change the column names in table, change the POCO property name, and use the column alias in SQL query to match those new property names.
I hope this helps you.
The problem was effectively the name of the properties.
I solved it using Custom Column Mapping to do it i got two possible solutions:
Without extensions
First, we define a Dictionary with the name of the column as key, and the name of the property as value
IDictionary<string, string> columnMaps = new Dictionary<string, string>()
{
{ "Macro_tarea.ID", "ID" },
{ "Registros.ID", "ID" }
};
Then, we define a delegate to obtain the PropertyInfo object of the property to which we intend to assign the alias of the previous dictionary
var mapper = new Func<Type, string, PropertyInfo>((type, columnName) =>
{
if (columnMaps.ContainsKey(columnName))
return type.GetProperty(columnMaps[columnName]);
else
return type.GetProperty(columnName);
});
Now, we define an object that implements the ITypeMap interface using CustomPropertyTypeMap implementation
ITypeMap MacroTareaMapper = new CustomPropertyTypeMap(typeof(Macro_tarea),
(type, columnName) => mapper(type, columnName));
ITypeMap RegistrosMapper = new CustomPropertyTypeMap(typeof(Registros),
(type, columnName) => mapper(type, columnName));
Then we register them
SqlMapper.SetTypeMap(typeof(Macro_tarea), MacroTareaMapper);
SqlMapper.SetTypeMap(typeof(Registros), RegistrosMapper);
Simpler solution with Dapper.FluentMap
It is implemented as follows:
We create a class that inherits from EntityMap<T> and using the Map method we define which column corresponds to each property. For example,
internal class Macro_tareaMap : EntityMap<Macro_tarea>
{
internal Macro_tareaMap()
{
//Mi propiedad ID esta asociada a la columna Macro_tarea.ID
Map(x => x.ID).ToColumn("Macro_tarea.ID");
}
}
Then just register it
FluentMapper.Initialize((config) =>
{
config.AddMap(new Macro_tareaMap());
});
Hope it helps another people!
Source: https://medium.com/dapper-net/custom-columns-mapping-1cd45dfd51d6
I've having a lot of trouble doing what you would assume a simple task.
Changing user IDs to username when showing a table at my web application.
This is the data retrieved form the table:(unnecessary information removed)
var data = (from o in _mainDbContext.blog
select new blogViewModel
{
Id = o.id,
title = o.title,
Editor= o.editorID,
} );
editorID is the user ID created by ASP.Net Identity system. I need to load the corresponding user name and place it inside Editor fields for all the entries fetched.
What I have tried so far:
Something like:
var data = (from o in _mainDbContext.blog
join u in _userManager.Users on o.editorID equals u.UserName
select new blogViewModel
{
Id = o.id,
title = o.title,
Editor= o.editorID,
} );
Doesn't work because EF Core doesn't support joining tables from different contexts.
Using foreach like:
foreach (var row in data)
{
var user = await _userManager.FindByIdAsync(row.editorID);
row.Editor= user.UserName;
}
Doesn't work. It doesn't change the information inside data.
Trying to use raw SQL did not help either. Because FromSql works only on one table and ExecuteSqlCommand does not work with SELECT.
Currently, EF Core don't support query multiple DbContext with one query. You could trace this behavior Query: Throw if second context instance is used in single query #11101.
For a workaround, you may consider convert _userManager.Users to _userManager.Users.ToList() which is a list object.
var data = from o in _mainDbContext.Blogs
join u in _userManager.Users.ToList() on o.EditorID equals u.Id
select new BlogViewModel
{
Id = o.Id,
Title = o.Title,
Editor = u.UserName
};
var result = data2.ToList();
Why are your contexts separated ? Why not merge them and create a relation with a navigation property between Users and Blogs.
public class MainDbContext: IdentityDbContext<AppUser>
{
public DbSet<Blog> Blogs { get; set; }
}
public class Blog
{
//Some properties
public int EditorId { get; set; }
public AppUser Editor { get; set; }
}
With this you can easily access user's info via the navigation property Editor.