Get all parent table rows and all children's table rows in ABP .NET core framework - aspnetboilerplate

I'm using built-in CRUD operations in ABP .NET Core but when it returned the data from the GetAll method, its retuned all parent rows, but return null in their list of children.
public class MainProjectAppService : AsyncCrudAppService<MainProject, MainProjectDto, int, PagedAndSortedResultRequestDto, MainProjectDto, MainProjectDto>
{
public MainProjectAppService(IRepository<MainProject, int> repositoryr) : base(repository)
{
}
}
-------------------------------
My Dto Code
[AutoMap(typeof(MainProject))]
public class MainProjectDto:EntityDto<int>
{
:
:
:
}

I think your problem is not a mapping problem. You should override CreateFilteredQuery of AsyncCrudAppService and include your detail list properties. AsyncCrudAppService GetAll calls this protected method. You can also add your additional linq queries from this method. If you do not include detail relations they are not included resulting actual sql query :
protected override IQueryable<YourEntity> CreateFilteredQuery(PagedAndSortedResultRequestDto input)
{
return base.CreateFilteredQuery(input)
.Include(p => p.YourDetailListProperty)
.Include(p => p.YourOtherDetailListProperty)
}

Related

Using the apicontroller to return odata.$count

I have controllers that inherit from ApiController. I have the EnableQuery attribute at the top of it to allow ODATA query strings. However $count does not work.
For eg:- ../products?$count=true does return data with no odataexception, but does not return the count. Any suggestions
You can get count separately and put it in new dictionary together with data, returning this dictionary in the method of your controller. The following code applies all that was specified in OData query (including select, expand and filter) and returns both data and count:
public class ProductsController : ApiController
{
public IHttpActionResult Get(ODataQueryOptions<Product> queryOptions)
{
IQueryable<Product> products = ... ///any IQueryable<Product>
var results = queryOptions.ApplyTo(products);
var dict = new Dictionary<string, object>();
if (Request.ODataProperties().TotalCount != null)
{
dict.Add("#odata.count", Request.ODataProperties().TotalCount);
}
dict.Add("value", results);
return Ok(dict);
}
}
However, to use ODATA in a way recommended by ASP.NET Web API documentation, you'll need to use ODataController instead of ApiController

Associating entities, not creating, with many-to-many relationships in EF Code First MVC3

In MVC3 Code First EF how do you associate one entity with another without creating a new one (many-to-many)?
So I have a many-to-many relationship between class1 and class2. I need class1 to hold many class2 and vice versa. However, class2 is independent; I have a list of them that I want to edit separately and then associate with a new class1.
When I pass my class2List to the controller( via AJAX and JSON), I checked and all the Ids of the class2s correspond to existing ids in the db, i.e. new class2s are not created.
Model
class
{
[key]
public int Id {set; get;}
}
class1 : class
{
private ICollection<class2> _class2s;
public virtual ICollection<class2> class2s
{
get { return _class2s ?? ( _class2s = new HashSet<class2>()); }
set { _class2s = value; }
}
}
class2 : class
{
private ICollection<class1> _class1s;
public virtual ICollection<class1> class1s
{
get { return _class1s ?? ( _class1s = new HashSet<class1>()); }
set { _class1s = value; }
}
}
Controller
public ActionResult SaveChanges(List<class2> class2List)
{
createNewClass2AndAssociateExistingClass2s(class2List);
SaveChangesToDb();
return View("ProductDetail", Model);
}
createNewClass2AndAssociateExistingClass2s(List<class2> class2List)
{
var newClass1 = newClass1()
{
class2s = class2List;
}
////UnitOfWork allows me to access several DbSets in one transaction
unitOfWork.Create(newClass1)
}
SaveChangesToDb()
{
unitOfWork.Commit();
}
What this does is create a new class1 (as it should) but instead of associating the existing class2s with it, it makes new class2s with new Ids and adds them to the database.
My question:
Does this have to do with how EF is reading my Id property from base class?
How would I be able to associate several existing class2s as a list with a new class1, without creating new class2s in the database?
Cheers
Ok so two things I learned from figuring this out:
I was inheriting from an abstract class when I should have been implementing an interface. This is a great idea if you have several entities that have a similar property such as "Id" and you want to do something like
T FindById<T>(int id) where T : IEntity
When making associations in EF, even if the Id matches an existing entry, it will not update that entry, unless EF is tracking that entry in the context, as it says here. What I needed to do was:
Add a method in the mapping layer that gets the entry by id that I
want from the repository
Copy the attributes of the new entry into that context entry
Return the context entry
Hope this helps someone

Moq testing LINQ Where queries

I'm using EF 4.1 to build a domain model. I have a Task class with a Validate(string userCode) method and in it I want to ensure the user code maps to a valid user in the database, so:
public static bool Validate(string userCode)
{
IDbSet<User> users = db.Set<User>();
var results = from u in users
where u.UserCode.Equals(userCode)
select u;
return results.FirstOrDefault() != null;
}
I can use Moq to mock IDbSet no problem. But ran into trouble with the Where call:
User user = new User { UserCode = "abc" };
IList<User> list = new List<User> { user };
var users = new Mock<IDbSet<User>>();
users.Setup(x => x.Where(It.IsAny<Expression<Func<User, bool>>>())).Returns(list.AsQueryable);
Initialization method JLTi.iRIS3.Tests.TaskTest.SetUp threw exception.
System.NotSupportedException: System.NotSupportedException: Expression
references a method that does not belong to the mocked object:
x => x.Where<User>(It.IsAny<Expression`1>()).
Other than creating a level of indirection (eg, using a ServiceLocator to get an object that runs the LINQ and then mock that method) I can't think of how else to test this, but I want to make sure there is no way before I introduce another layer. And I can see this kind of LINQ queries will be needed quite often so the service objects can quickly spiral out of control.
Could some kind soul help? Thanks!
There is an article on MSDN highlighting how to mock using moq:
The gist of it is to represent linq to entities operations with linq to objects.
var mockSet = new Mock<DbSet<Blog>>();
mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
As Ladislav points out there are disadvantages to this as Linq To Objects is simply different to Linq to Entities so it may result in false positives. But it now being an MSDN article it does point that it is at least possible and perhaps recommended in some cases?
One thing that may of changed since the original answers to this post is that the Entity Framework team have opened up areas of Entity Framework in EF 6.0 to make it easier to mock it's inners.
Although I have not tried this, because IDBSet implements IEnumerable you might have to mock the enumerator method so the linq statements will pick up your list of users. You don't actually want to mock linq but by the looks of your code you want to test whether you are finding the right user based on the UserCode which I think is a valid unit test.
var user = new User { UserCode = "abc" };
var list = new List<User> { user };
var users = new Mock<IDbSet<User>>();
users.Setup(x => x.GetEnumerator()).Returns(list.GetEnumerator());
You might get a conflict with the non-generic version of the GetEnumerator but it this might help you on the right track.
Then you have to then place the mocked object on the data context which depends on other code that we don't see.
As I know Moq is able to set up only virtual methods of mocked object itself but you are trying to set up extensions (static) method - no way! These methods are absolutely outside of your mock scope.
Moreover that code is hard to test and requires too much initialization to be able to test it. Use this instead:
internal virtual IQueryable<User> GetUserSet()
{
return db.Set<User>();
}
public bool Validate(string userCode)
{
IQueryable<User> users = GetUserSet();
var results = from u in users
where u.UserCode.Equals(userCode)
select u;
return results.FirstOrDefault() != null;
}
You will just need to set up GetUserSet to return your list. Such testing has some major issues:
You are not testing the real implementation - in case of EF mocking sets is stupid approach because once you do it you change linq-to-entities to linq-to-objects. Those two are totally different and linq-to-entities is only small subset of linq-to-objects = your unit tests can pass with linq-to-objects but your code will fail at runtime.
Once you use this approach you cannot use Include because include is dependent on DbQuery / DbSet. Again you need integration test to use it.
This doesn't test that your lazy loading works
The better approach is removing your linq queries from Validate method - just call them as another virtual method of the object. Unit test your Validate method with mocked query methods and use integration tests to test queries themselves.
I found it easier just to write the stub:
internal class FakeDbSet<T> : IDbSet<T>where T : class
{
readonly HashSet<T> _data;
readonly IQueryable _query;
public FakeDbSet()
{
_data = new HashSet<T>();
_query = _data.AsQueryable();
}
public virtual T Find(params object[] keyValues)
{
throw new NotImplementedException("Derive from FakeDbSet<T> and override Find");
}
public T Add(T item)
{
_data.Add(item);
return item;
}
public T Remove(T item)
{
_data.Remove(item);
return item;
}
public T Attach(T item)
{
_data.Add(item);
return item;
}
public void Detach(T item)
{
_data.Remove(item);
}
Type IQueryable.ElementType
{
get { return _query.ElementType; }
}
Expression IQueryable.Expression
{
get { return _query.Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return _query.Provider; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _data.GetEnumerator();
}
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
{
return Activator.CreateInstance<TDerivedEntity>();
}
public T Create()
{
return Activator.CreateInstance<T>();
}
public ObservableCollection<T> Local
{
get
{
return new ObservableCollection<T>(_data);
}
}

How can i load collection with include

How can I load collection with include, I have tried these:
Type.Where(t => t.Entity.Where(e => e.Parent == false).Count() > 0).Include(e => e.Entity)
But the filter is not supported here (Entities recovered not fulfill a condition).
With LinQ To Entities :
var v = from type in ObjectContext.Type
from entity in Type.Entities
where entity.Parent == false
select type;
But type do not contains it's associated Entities.
I can't write select new Type { type.Code, type.Entities } and I can't use anonymous type as it's not adapted for my repository layer.
Have you any ideas on how I can get a list of Type objects with its property Entities satisfying a condition?
Update
Thank you for your response
I think it's not a good idea to use the CTP , no ?
I am sorry, I did not explain my object model, I have :
class Type {
public string Code { get; set; }
public IList<Entity> Entities { get; set; }
...
}
class Entity {
public string Code { get; set; }
...
}
And I want to use your second filtering proposal : Filter both entity sets
Do you have any suggestions without using the CTP ?
Thanks
Rad
as you stated you want to apply filters to the related entities, best to look at the new CTP5 of the EF4 http://blogs.msdn.com/b/adonet/archive/2011/01/31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx
Look under "Applying filters when explicitly loading related entities"

NotSupportedException when using lambda against a DataContext and a generic function constraint of interface

I'm not entirely sure the title is properly worded, but here's the situation... I noticed today while trying to create a generic save function for Linq to Sql that when I use lambda against a data context select. It breaks within a generic function with a type constraint of another generic interface. However, it works fine with LINQ syntax. I'm posing this question more or less to get an understanding of why this happens.
The exception being thrown is:
NotSupportedException
The mapping of interface member IEntity`1.ID is not supported.
The code involved looks similar to this (simplified):
The error causing function is GenericFunc<T>
//Assume SomeTable is generated from a DBML L2S file.
//partial extension of the L2S class
public partial class SomeTable : IEntity<SomeTable> { }
//interface to expose shared properties in these entity classes
public interface IEntity<T> where T : class
{
//isolated application, int Primary key for all these tables.
int ID { get; set; }
}
//simple helper class for L2S stuff.
public class Repository
{
//helper for inserting/updating data..
public void SaveSomeTable(SomeTable data)
{
SomeDataContext db = new SomeDataContext();
GenericFunc<SomeTable>(db.SomeTables, data);
}
//toy function for this example
public void GenericFunc<T>(System.Data.Linq.Table<T> table, T data) where T : class, IEntity<T>, new ()
{
//note the generic type constraints...
//in this case, all table entities conform to the IEntity<T> class.
//breaks
var row = table.SingleOrDefault(o => o.ID == data.ID);
//works
var row1 = (from item in table where item.ID == data.ID select item).SingleOrDefault();
//... more stuff.
}
}
This isn't really a lambda issue. Here's non-query-syntax which will work too:
var row1 = table.Where(o => o.ID == data.ID)
.SingleOrDefault();
That compiles to the same code as the query expresion, basically.
I strongly suspect it's a different code path for SingleOrDefault (internally) which gives problems.

Resources