Doing a Join Query In ASP.NET MVC 3 using Sharp Lite Architecture - asp.net-mvc-3

I'll use the mystore sharp lite architecture example. You can download the solution here:
https://github.com/codai/Sharp-Lite
So there are two entities in question. Products. And ProductCategories.
Products looks like this:
public class Product : Entity
{
public Product() {
Categories = new List<ProductCategory>();
}
[DomainSignature]
[Required(ErrorMessage = "Name must be provided")]
[StringLength(255, ErrorMessage = "Name must be 255 characters or fewer")]
public virtual string Name { get; set; }
/// <summary>
/// Money is a component, not a separate entity; i.e., the Products table will have column
/// for the amount
/// </summary>
[DataType("Money")]
public virtual Money Price { get; set; }
/// <summary>
/// many-to-many between Product and ProductCategory
/// </summary>
[Display(Name="Product Categories")]
public virtual IList<ProductCategory> Categories { get; protected set; }
}
Notice it has the ProductCategories List in it. So it does have a list of ProductCategories that applies to it.
And here is ProductCategory:
public class ProductCategory : Entity
{
public ProductCategory() {
Children = new List<ProductCategory>();
Products = new List<Product>();
}
[DomainSignature]
[Required(ErrorMessage="Name must be provided")]
[StringLength(255, ErrorMessage="Name must be 255 characters or fewer")]
public virtual string Name { get; set; }
/// <summary>
/// many-to-one from child ProductCategory to parent ProductCategory
/// </summary>
[Display(Name="Parent Category")]
public virtual ProductCategory Parent { get; set; }
/// <summary>
/// many-to-many between ProductCategory and Product
/// </summary>
public virtual IList<Product> Products { get; protected set; }
/// <summary>
/// one-to-many from parent ProductCategory to children ProductCategory
/// </summary>
public virtual IList<ProductCategory> Children { get; protected set; }
}
I understand queries enough that I have made simple ones with where statements. For example this is the query I used in another program to search for the first name of a customer:
public static IQueryable<Customer> GetByFirstName(this IQueryable<Customer> customers, string name)
{
name = name.ToUpper();
return
customers.Where(c => c.BillingAddress.FirstName.ToUpper().Contains(name));
}
But I don't quite understand joins yet. Can someone show me the light?

First of all, if you don't understand how to make a Join, you should probably consult some docs on it, but keep in mind, that this has very little (if nothing) to do with Sharp Lite.
The basic idea behind Sharp Lite's repsitories is to use the power of IQueryable in order to depend less on the underlying data access (NH or EF which are the most populars), so you should basically learn how joins work on NH (if you are using NH) and start working using that. Also, try to put up a better example with a more complex structure so you can really do some joins.
If you need a jumpstart on SharpLite, make sure to read the post on why Sharp lite exists and the other explaining how it's built. Also, I made one myself to get people started with the demo project.
Hope I can help!

Related

EF Core 5 Duplicating Records in the Many-to-Many Relationship

I'm having trouble implementing the many-to-many relationship using the Entity Framework Core 5 in Visual Studio.
I have the classes:
public class Medico
{
public Medico()
{
this.Especialidades = new HashSet<Especialidade>().ToList();
}
public int Id { get; set; }
public string Nome { get; set; }
public int CRM { get; set; }
public List<Especialidade>Especialidades { get; set; }
public class Especialidade
{
public int Id { get; set; }
public string Descricao { get; set; }
public IList<Medico>Medicos { get; set; }
}
And the Create method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Nome,CRM")] Medico medico)
{
var lstTags = Request.Form["chkTags"];
if (!string.IsNullOrEmpty(lstTags))
{
int[] splTags = lstTags.ToString().Split(',').Select(Int32.Parse).ToArray();
if (splTags.Count() > 0)
{
var medicoEspecialidades = await _context.Especialidades.Where(t => splTags.Contains(t.Id)).ToListAsync();
foreach (var me in medicoEspecialidades)
{
medico.Especialidades.Add(me);
}
}
}
if (ModelState.IsValid)
{
_context.Medicos.Add(medico);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(medico);
}
But when I run Create, it returns me with the following error:
"Cannot insert explicit value for identity column in table 'Especialidades' when IDENTITY_INSERT is set to OFF."
If I turn off the Identity_Insert of the Specialty table in the bank, it even inserts, but duplicates the records in the Specialty table.
I've been researching and trying to find a solution for 2 days now. Can someone who has been through this give me a hand?
The application source code is here: https://www.dropbox.com/s/xn6b95h7amfpuqa/AppCompleta%205.0.rar?dl=1
The approach looks Ok, though I would check to ensure that the medico being passed in does not have any Especialidade somehow coming in from the client as these would be detached entities. The error seems to imply that Medico may have a detached Especialidade in its collection. If the checked values represents everything that should be tracked, then this collection should be cleared and the Especialidade references added.
Do you have any explicit mapping configuration for either of these entities? If not I would highly recommend using one for Many-to-Many relationships as sometimes EF can default to unexpected schema assumptions when working off convention, especially in Code First if that is the case. I would look at your schema carefully to ensure it is matching what would be expected for a Many-To-Many. For example, what is the linking table name for Medico-Especialidade? Is there an entity defined for it in the configuration? This is entirely optional and EF should work it out, however if you do have explicit mapping that might not be configured correctly, tripping up the relationships.
One other detail giving off a smell:
public Medico()
{
this.Especialidades = new HashSet<Especialidade>().ToList();
}
public List<Especialidade>Especialidades { get; set; }
This should be:
public ICollection<Especialidade> { get; set; } = new HashSet<Especialidade>();
EF can work with lists, but when it comes to proxies and the behind the scenes EF is doing with entities it is generally better to declare your collection references as ICollection rather than concrete classes. ToListing a HashSet merely produces a List, so either = new HashSet<Especialidade>() or = new List<Especialidade>() will do. The difference would merely be the behaviour of the collection when you are populating it after "newing" up a Medico, or deserializing one.

Xamarin: why need the BusinessEntityBase class

I want to learn Xamarin and i took a look at a sample project called 'Tasky'
But i don't understand why theres a BusinessEntityBase class...
A task also needs it's ID to be PK and incremented so why doesn't it implement
the BusinessEntityBase class instead of the IBusinessEntity interface?
public class Task : IBusinessEntity
{
public Task ()
{
}
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string Name { get; set; }
public string Notes { get; set; }
// new property
public bool Done { get; set; }
}
public abstract class BusinessEntityBase : IBusinessEntity
{
public BusinessEntityBase ()
{
}
/// <summary>
/// Gets or sets the Database ID.
/// </summary>
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
}
public interface IBusinessEntity
{
int ID { get; set; }
}
IBusinessEntity interface is just that - an interface, the properties of which (in this case, ID) would be a part of every business entity. Read up on the use of Interfaces in C# to get a better understanding of why this is done.
Another example would be:
Lets say you have an employee management application which contains three different kinds of users - Manager, Developer, Tester. You have a class for each of these.
It is very likely that all three of them contain an ID field, a first name and a last name.
Instead of adding the same properties to each of their classes, you create an interface called IEmployee, which has three fields - ID, FirstName and LastName and get each of the three classes to implement it.
Functionally, implementing the properties on an interface and adding them manually has the same effect on the class. Although, having an interface connecting all three of them, you now have a more abstract way to access your data (For example, to count the number of employees, you could check the count of IEmployee objects, rather than counting all three separate and then adding the numbers up).
TL;DR Doing it this way is not mandatory. In this scenario you could simply have a BusinessEntity class that has an ID field. It is simply a good practice and makes your applications easy/possible to maintain when they grow.

Missing inverse property in asp.net webapi odata $metadata

have very simple relationship between two entities and I am trying to expose them with asp.net webapi odata controllers but it seems that something is wrong with $metadata.
When I run jaydatasvcutil.exe on the $metadata I get warning: inverseProperty other side missing.
When I use breezejs loadNavigationProperty I get similar error.
I have the problem even with official example.
http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/working-with-entity-relations
You can observe the $metadata here http://sdrv.ms/Z5Klfw
Please help.
When we are generating navigation properties we don't reuse the relationships.
For example, lets say you have simple model,
public class Product
{
public int Id { get; set; }
public Supplier Supplier { get; set; }
}
public class Supplier
{
public int Id { get; set; }
public Product[] Products { get; set; }
}
The $metadata for the navigation properties that we generate looks like this,
<NavigationProperty Name="Supplier" Relationship="ProductsService.Models.ProductsService_Models_Product_Supplier_ProductsService_Models_Supplier_SupplierPartner" ToRole="Supplier" FromRole="SupplierPartner" />
<NavigationProperty Name="Products" Relationship="ProductsService.Models.ProductsService_Models_Supplier_Products_ProductsService_Models_Product_ProductsPartner" ToRole="Products" FromRole="ProductsPartner" />
Notice that we are generating two relationships instead of one. The reason we do that is that it is a hard problem to figure out if two navigation properties represent the same relationship. Take the instance of Product and Manufacturer.
public class Manufacturer
{
public int Id { get; set; }
public Product[] RawMaterials { get; set; }
public Product[] Produces { get; set; }
}
public class Product
{
public int Id { get; set; }
public Manufacturer[] Producers { get; set; }
public Manufacturer[] Consumers { get; set; }
}
It is not trivial to figure out that Maufacturer.RawMaterials and Product.Consumers should share the same relationship and Manufaturer.Produces and Product.Producers should share the same relationship. We chose not to do it because the clients that we know of don't make much out of this information.
All this happens because OData uses the same EDM model as the entityframework. Entityframework requires this information as it maps these relationships to association sets which would become tables in the database.
Another reason we chose not to do it is that this could be going away in OData V4. Check out the working draft here (page 23 and page 57 would be of interest). In short, navigation properties in $metadata in OData V4 would look more like this,
<NavigationProperty Name="Category" Type="Self.Category" Nullable="false" Partner="Products" />
Notice that there is no relationship and there would be no association sets.

How to make single controller for two database classes - MVC3

I have two database classes as defined below:
public class TopDate
{
[Key]
public int DateId { get; set; }
public DateTime Date { get; set; }
}
public class TopSong
{
[Key]
public int SongId { get; set; }
public string Title { get; set; }
public int DateId { get; set; }
}
where DateId is foreign key to TopSong
I am creating a controller through which i can create, delete or edit these database values.
When i right click on controller class and add controller i can only select one of the two classes defined above. Is there a way to make 1 controller to handle database updates to both these tables on one page?
Error Image:
Your controller should not be dealing directly with domain objects (meaning those things that are directly associated with your database). Create a ViewModel that contains the properties that you need, use your service layer to populate the ViewModel and your controller will use that as the Model for its base. An example of your ViewModel could be something like the following given your description above:
public class MusicViewModel
{
public int SongId {get;set;}
public string Title {get;set;}
public IEnumerable<DateTime> TopDates {get;set;}
}
This view model would contain a list of all dates that a specific song was a Top Song.
The objects you showing (code) are database classes (so called domain objects).
What you need to do is to define a view model, a standard ASP MVC practice:
you define a class, that is tailored for specific view and only containing data relevant to that particular view. So you will have a view model for a view that will create a song, another that will update it etc.
Actually situation you describing is classical situation to use view models. Using domain objects in the views, however, is really really bad practice and prone to more problems than you want to deal with.
Hope this helps.

loosely coupled development

I'm reading Sanderson's "Pro ASP.NET MVC Framework".
I'm confused a little with decoupling implementation.
He uses LinqToSql in the code sample and repository pattern to interact with database.
[Table(Name = "Products")]
public class Product
{
[Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync=AutoSync.OnInsert)]
public int ProductID { get; set; }
[Column]
public string Name { get; set; }
[Column]
public string Description { get; set; }
[Column]
public decimal Price { get; set; }
[Column]
public string Category { get; set; }
}
public class SqlProductsRepository : IProductsRepository
{
private Table<Product> productsTable;
public SqlProductsRepository(string connectionString)
{
productsTable = (new DataContext(connectionString)).GetTable<Product>();
}
public IQueryable<Product> Products
{
get { return productsTable; }
}
}
SqlProductsRepository is dataLayer here as it interacts with database.
1.However it is located in DomainModel project. Maybe it is just for demo?
So where is domain logic here?
2.I can't see full decoupling as Products property return IQueryable.
Is it assumed that if we change a component, it must contain Product class?
I seem that it is required to have one more project with abstractions:
Repository Interfaces such as IProductRepository and MappingClasses interfaces such as IProduct.
DataLayer component must implement these abastractions.
Is it right?
Maybe it is diffucult to explain it shortly, however how it is usually work in live projects?
IMHO, this must have been for demo purposes as it doesn't make sense (in real world environments) to separate your architecture in layers and keep these different layers in a single dll. I just came up with a valid reason. What if you want multiple applications to use your business layer without immediate access to the datalayer. You'd have to carefully consider access modifiers to your datalayer but it would be possible.
Whether you should expose IQueryable objects from your datalayer is a discussion that has been going on since the invention of the repository pattern. And there are quite a lot of resources to be found about it.
To list a few:
http://mikehadlow.blogspot.com/2009/01/should-my-repository-expose-iqueryable.html
How can I write a clean Repository without exposing IQueryable to the rest of my application?
To return IQueryable<T> or not return IQueryable<T>
http://www.weirdlover.com/2010/05/11/iqueryable-can-kill-your-dog-steal-your-wife-kill-your-will-to-live-etc/
... (google)

Resources