I am using dapper extensions and have a question about the class mapper. Unfortunately most of my tables need some mapping done to them such as different schemas etc.
So I find I am typically doing a lot of swapping out the DefaultMapper as per below:
public Hierarchies HierarchyGetByName(string aName)
{
Hierarchies result;
using (SqlConnection cn = GetSqlConnection())
{
cn.Open();
Type currModelMapper = DapperExtensions.DapperExtensions.DefaultMapper;
try
{
DapperExtensions.DapperExtensions.DefaultMapper = typeof(HierarchiesMapper);
IFieldPredicate predicate = Predicates.Field<Hierarchies>(f => f.Name, Operator.Eq, aName);
result = cn.GetList<Hierarchies>(predicate).FirstOrDefault();
}
finally
{
DapperExtensions.DapperExtensions.DefaultMapper = currModelMapper;
}
cn.Close();
}
return result;
}
If I access 2 tables then I have to do this twice for instance.
Is there a way to add all the mapper classes at once to say a collection and depending on the table being accessed the correct one is chosen?
You could add a set of classes within your app that apply custom remapping to your entities. For example these 3 empty classes apply the PrefixDapperTableMapper to the Profile and FileNotificationAdhocRecipient classes while the AnotherDifferentTypeOfDapperClassMapper is applied to NotificationProfile.
public class ProfileMapper : PrefixDapperTableMapper<Domain.Entities.Profile>
{
}
public class FileNotificationAdhocRecipientMapper : PrefixDapperTableMapper<Domain.Entities.FileNotificationAdhocRecipient>
{
}
public class NotificationProfileMapper : AnotherDifferentTypeOfDapperClassMapper<Domain.Entities.NotificationProfile>
{
}
and your actual mapping code exists in separate mappers (I've not shown AnotherDifferentTypeOfDapperClassMapper but that would be similar to below)
public class PrefixDapperTableMapper<T> : ClassMapper<T> where T : class
{
public PrefixDapperTableMapper()
{
AutoMap();
}
//name or schema manipulations in some overrides here.
}
As long as they're in the same assembly, DapperExtensions will find and use them or you can set the mapping assembly with code similar to:
DapperExtensions.DapperExtensions.SetMappingAssemblies({ typeof(ProfileMapper ).Assembly })
Related
Working with database-first approach creating ASPNETCORE MVC web app with user authentication, I would like to override the way the parameters from IdentityUser class are queried to the database. The reason is the current implementation of IdentityUser has two new parameters NormalizedEmail and NormalizedUserName (which in my opinion retracts from Normalization).
Is there a way I can write the code below in the Model class so that those two parameters are not included in the query to the database or is that something that needs to be done in the controller class?
public class IdentityUser : Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUser
{
public override string NormalizedUserName
{ get { return null; } set { value = null; } }
public override string NormalizedEmail
{ get { return null; } set { value = null; } }
}
Not far as I can tell, both parameters are part of the data model and as explained in this Issue #351
About Identity 3.0:
...Instead we compute a normalized representation of the user name and we
store it in a separate column so that lookups by normalized user name
should now be sargable.
So in other words, if you "override the way the parameters from IdentityUser class are queried to the database" in essence you'll be doing exactly the opposite the class intends to do.
I can't find any documentation or examples I can follow to use the SqlBuilder class.
I need to generate sql queries dynamically and I found this class. Would this be the best option?
the best place to start is to checkout the dapper source code from its github repo and have a look at the SqlBuilder code. The SqlBuilder class is only a 200 lines or so and you should be able to make an informed choice on whether it is right for your needed.
An other option is to build your own. I personally went down this route as it made sense. Dapper maps select querys directly to a class if you name your class properties the same as your database or add an attribute such as displayName to map from you can use reflection to get the property names. Put there names and values into a dictionary and you can genarate sql fairly easy from there.
here is something to get you started:
first an example class that you can pass to your sqlbuilder.
public class Foo
{
public Foo()
{
TableName = "Foo";
}
public string TableName { get; set; }
[DisplayName("name")]
public string Name { get; set; }
[SearchField("fooId")]
public int Id { get; set; }
}
This is fairly basic. Idea behind the DisplayName attribute is you can separate the properties out that you want to include in your auto generation. in this case TableName does not have a DisplayName attribute so will not be picked up by the next class. however you can manually use it when generating your sql to get your table name.
public Dictionary<string, object> GetPropertyDictionary()
{
var propDictionary = new Dictionary<string, object>();
var passedType = this.GetType();
foreach (var propertyInfo in passedType.GetProperties())
{
var isDef = Attribute.IsDefined(propertyInfo, typeof(DisplayNameAttribute));
if (isDef)
{
var value = propertyInfo.GetValue(this, null);
if (value != null)
{
var displayNameAttribute =
(DisplayNameAttribute)
Attribute.GetCustomAttribute(propertyInfo, typeof(DisplayNameAttribute));
var displayName = displayNameAttribute.DisplayName;
propDictionary.Add(displayName, value);
}
}
}
return propDictionary;
}
This method looks at the properties for its class and if they are not null and have a displayname attribute will add them to a dictionary with the displayname value as the string component.
This method is designed to work as part of the model class and would need to be modified to work from a separate helper class. Personally I have it and all my other sql generation methods in a Base class that all my models inherit from.
once you have the values in the dictionary you can use this to dynamically generate sql based on the model you pass in. and you can also use it to populate your dapper DynamicParamaters for use with paramiterized sql.
I hope this helps put you on the right path to solving your problems.
This question is similar to Where does business logic sit in MVVM?
However, I didn't want to create a comment chain on that one
Lets say for example that I have a table of invoices and I want to get this data and perform some apportionment on it ready for use in 2 totally separate reports and 3 screens.
In our current web application I would have put this in the Data Service Layer, and all of my reports and screens would have called this
In MVVM people seem to suggest that the model should not be bloated out and that logic should be put in the view models. But in this case I be duplicating the code 5 times?
In his answer to my other question Reed states "Anything that's specific to the domain or business should be reusable by other applications, using other architectures."
Can Reed or someone clarify what my approach should be? Can MVVM be combined with other architectures?
I am using Silverlight 5 with the Simple MVVM Toolkit
Paul
The ViewModel is not for business logic. It is for user interface logic. As its name is given, it is representing the View. If you have five different reports that presents similar data, give this data a name and make all five ViewModels understand how to work with the data objects.
Since we are talking about reports, data is understood to e only displayed. The ViewModel can just be a simple data source with minimal user interface interaction, basically a very thin layer.
I faced the same problem and decided to go this way:
I created classes like controllers in MVC (performing some actions with my model) and I work with them in all ViewModels.
For example: our application has a list of books. We need to add/edit/delete them.
So we have a model:
public class Book {
public int BookId { get; set; }
public string Title { get; set; }
public string Author { get; set; }
}
Then we have a controller class:
public class BookController {
string dbPath = ...;
public void AddBook(string title, string author)
{
var book = new Book() { Title = title, Author = author };
AddBook(book);
}
public void DeleteBook(int id)
{
using (var db = new SQLiteConnection(dbPath))
{
db.Delete<Book>(id);
}
}
public void DeleteBook(Book book)
{
using (var db = new SQLiteConnection(dbPath))
{
DeleteBook(book.BookId);
}
}
public List<Book> GetAllBooks()
{
using (var db = new SQLiteConnection(dbPath))
{
return db.Table<Book>().ToList();
}
}
public Book FindBook(string title, string author, int id)
{
.....
}
}
Now we can use it wherever we need, e.g.:
public class BookListViewModel : ViewModelBase {
public BookListViewModel() {
GetData();
}
void GetData()
{
BookController bc = new BookController(); // here we start using our controller.
_books = new List<Book>();
_books = bc.GetAllBooks();
}
}
Such approach helps us:
1) keep all business logic separately (in controller class)
2) avoid code duplication
For the love of heaven and earth I really wish someone could help me out with this issue. It seems everyone has something to say about EF but nothing about Linq-to-SQL.
I am trying to grab some data from my table via a stored procedure, believe me, that's all.
I added the Linq-to-SQL model (LAMP.dbml)
added the stored procedure (getAffectedParcel) from the server explorer. getAffectedParcel takes 2 strings as parameters
Build the application.
Added a domain service class (LAMPService)
Selected the (LAMPDataContext) as the data context class (normally I would tick generate metadata, but since I am not working with tables it's not enabled for ticking)
Added the following function to the LAMPService.cs:
public IEnumerable < getAffectedParcelResult > GetTheAffectedParcels(String v, String vf)
{
return this.DataContext.getAffectedParcel(v, vf).AsEnumerable();
}
Added the following code to a Silverlight page in an attempt to consume the stored procedure:
LAMPContext db = new LAMPContext();
try
{
var q = db.GetTheAffectedParcels("18606004005", "").Value;
foreach (getAffectedParcelResult GAP in q)
{
MessageBox.Show(GAP.Owner);
}
}
catch (Exception ex)
{
MessageBox.Show (ex.Message.ToString());
}
Build and run application. An error occurs stating:
Object reference not set to an instance of an object.
I have tried ~1000,000 ways to see if this thing would work, but to no avail. Please don't tell me to use Entity Framework, I want to use Linq-to-SQL. Can someone (anyone) help me out here.
//houdini
Calling a stored procedure from the Silverlight client happens in the Async world. Let's consider an example from the AdventureWorks database...
Here's what the Domain Service method looks like. It is calling the EF on a stored procedure in the database called 'BillOfMaterials'.
public IQueryable<BillOfMaterial> GetBillOfMaterials()
{
return this.ObjectContext.BillOfMaterials;
}
Back on the client side, here is the code for setting up the call...
public GetSp()
{
InitializeComponent();
DomainService1 ds1 = new DomainService1();
var lo = ds1.Load(ds1.GetBillOfMaterialsQuery());
lo.Completed += LoCompleted;
}
First, the Domain Service is created, and then it is used to load the results of the stored procedure. In this particular case, the result of this is an instance of 'LoadOperation'. These things are async, so the LoadOperation needs to have a callback for when it is finished. The callback code looks like this...
public ObservableCollection<BillOfMaterial> MyList { get; set; }
void LoCompleted(object sender, EventArgs e)
{
LoadOperation lo = sender as LoadOperation;
if(lo!=null)
{
MyList = new ObservableCollection<BillOfMaterial>();
foreach(BillOfMaterial bi in lo.AllEntities)
{
MyList.Add(bi);
}
dataGrid1.ItemsSource = MyList;
}
}
In this method, the 'sender' is dereferenced into the LoadOperation instance, and then all the goodies from the database can be accessed. In this trivial example, a list is built and passed to DataGrid as the ItemsSource. It's good for understanding, but you would probably do something else in practice.
That should solve your problem. :)
The best advice I can give on Silverlight and RIA is never do ANYTHING on your own until you have tried it in AdventureWorks. You will just waste your time and beat your head against the wall.
Firstly, it seems like your DomainService code is written for Invoke() rather than Query(). You should use Query as it enables you to update data back to the server.
Solution: you should add a [Query] attribute to GetTheAffectedParcels on the domain service.
[Query]
public IQueryable<Parcel>
GetTheAffectedParcels(string ParcelNumber, string LotNumber)
{
// etc.
}
Secondly, RIA Services needs to know which is the primary key on the Parcel class.
Solution: Apply a MetadataType attribute to the Parcel class, which allows you to add metadata to the Parcel class indirectly, since it is generated by Linq2Sql and you couldn't add annotations directly to the ParcelId - it'd get wiped away.
[MetadataType(typeof(ParcelMetadata)]
public partial class Parcel
{
}
public class ParcelMetadata
{
[System.ComponentModel.DataAnnotations.Key]
public int ParcelId {get; set; }
}
Thirdly, modify your client like this. Instead try this on the Silverlight client:
LAMPContext db = new LAMPContext();
try
{
var q = db.GetTheAffectedParcelsQuery("18606004005", "");
db.Load(q, (op) =>
{
if (op.HasError)
{
label1.Text = op.Error.Message;
op.MarkErrorAsHandled();
}
else
{
foreach (var parcel in op.Entities)
{
// your code here
}
}
}
}
catch (Exception ex)
{
label1.Text = op.ex.Message;
}
Much thanks to Chui and Garry who practically kicked me in the right direction :) [thanks guys...ouch]
This is the procedure I finally undertook:
-After adding the data model(LINQ2SQL) and the domain service, I created a partial class [as suggested by Chui] and included the following metadata info therein:
[MetadataTypeAttribute(typeof(getAffectedParcelResult.getAffectedParcelResultMetadata))]
public partial class getAffectedParcelResult
{
internal sealed class getAffectedParcelResultMetadata
{
[Key]
public string PENumber { get; set; }
}
}
Then, Adjusted the Domain Service to include the following:
[Query]
public IQueryable<getAffectedParcelResult> GetTheAffectedParcels(string v, string vf)
{
// IEnumerable<getAffectedParcelResult> ap = this.DataContext.getAffectedParcel(v, vf);
return this.DataContext.getAffectedParcel(v, vf).AsQueryable();
}
Then Build the app, afterwhich the getAffectedParcelResult store procedure appeared in the Data Sources panel. I wanted to access this via code however. Therefore, I accessed it in silverlight [.xaml page] via the following:
LAMPContext db = new LAMPContext();
var q = db.GetTheAffectedParcelsQuery("18606004005", "");
db.Load(q, (op) =>
{
if (op.HasError)
{
MessageBox.Show(op.Error.Message);
op.MarkErrorAsHandled();
}
else
{
foreach (getAffectedParcelResult gap in op.Entities)
{
ownerTextBlock.Text = gap.Owner.ToString();
}
}
},false);
This worked nicely. The thing is, my stored procedure returns a complex type so to speak. As of such, it was not possible to map it to any particular entity.
Oh and by the way this article helped out as well:
http://onmick.com/Home/tabid/154/articleType/ArticleView/articleId/2/Pulling-Data-from-Stored-Procedures-in-WCF-RIA-Services-for-Silverlight.aspx
I want to add a bool variable and property to the base Entity class in my RIA services project so that it is available throughout all the entity objects but seem unable to work out how to do this. I know that adding properties to actual entities themselves is easy using .shared.cs and partial classes but adding such properties to the Entity class using similar methods doesn't work.
For example, the following code doesn't work
namespace System.ServiceModel.DomainServices.Client
{
public abstract partial class Entity
{
private bool auditRequired;
public bool AuditRequired
{
get { return auditRequired; }
set { auditRequired = value; }
}
}
}
All that happens is that the existing Entity class gets totally overriden rather than extending the Entity class.
How do I extend the base Entity class so that functionality is available thoughout all derived entity classes?
You won't be able to add a property to the Entity class. This class is already compiled and cannot be modified (partial classes only work because your have the source code of the class in your solution and the code can be merged at compile time).
One option may be to create a class that inherits from Entity, then add your property in this class, and have your entities inherit from your custom class instead of Entity. This might not be practical for use with designers, though.
public class MyEntityBase : Entity
{
private bool auditRequired;
public bool AuditRequired
{
get { return auditRequired; }
set { auditRequired = value; }
}
}
public class Entity1 : MyEntityBase
{
}