I am currently trying to setup work with MySQL db via dapper.
I have declared POCO for dapper:
public class DevicesOnTesterDbTable
{
public string UUID { get; set; }
public string DeviceType { get; set; }
public string DeviceAddedAt { get; set; }
public uint Address { get; set; }
}
I also have Dapper.Fluent EntityMap for that table:
public class DevicesOnTesterDbTableMap : EntityMap<DevicesOnTesterDbTable>
{
public DevicesOnTesterDbTableMap()
{
Map(p => p.UUID).ToColumn("devices_on_tester_uuid");
Map(p => p.DeviceType).ToColumn("devices_on_tester_type");
Map(p => p.DeviceAddedAt).ToColumn("devices_on_tester_add_at");
Map(p => p.Address).ToColumn("devices_on_tester_address");
}
}
My database have about ten tables so i have ten pairs of POCO and EntityMap classes for them. So in case of reading i have to perform something like this for each table:
public static List<DevicesOnTesterDbTable> ReadDevices(string server)
{
FluentMapper.Initialize(config =>
{
config.AddMap(new DevicesOnTesterDbTableMap());
});
try
{
using (var mySqlConnection = OpenConnection(server))
{
mySqlConnection.Open();
return mySqlConnection.Query<DevicesOnTesterDbTable>("Select * from power_source_calibration").ToList();
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
Now is there a way to pass this pairs into some other method that will perform common operations such as read\write\update etc? Or maybe there is some better way around?
There is a better way around; actually you are doing it wrong.
You do not need to call FluentMapper.Initialize in each of your method like ReadDevices. Ideally, all your mappings (for all entities) should happen only once at the startup of your application.
Following is from here:
Initialize your mapping at the start of your application.
FluentMapper.Initialize(config =>
{
config.AddMap(new InvoiceMap());
});
Also, refer this question which shows how to do it at application startup. Refer Register() method which is static and caller call it once at application startup somewhere.
Related
Im using the Graphql .Net library to build a GraphQl API.
The following is a domain example of what we currently have, where, the area has a list of sampling point identifiers:
public class AreaRoot {
public String Id { get; set; }
public List<String > SamplingPointIds { get; set; }
}
public class SamplingPointRoot {
public String Id { get; set; }
public String Description { get; set; }
}
And the types are defined as follow:
public class AreaType : ObjectGraphType<AreaRoot>
{
public AreaType()
{
Name = "Area";
Field(x => x.Id, type: typeof(IdGraphType));
Field(x => x.SamplingPointIds, type: typeof(ListGraphType<StringGraphType>));
}
}
public class SamplingPointType : ObjectGraphType<SamplingPointRoot>
{
public SamplingPointType()
{
Name = "SamplingPoint";
Field(x => x.Id, type: typeof(IdGraphType));
Field(x => x.description, type: typeof(StringGraphType));
}
}
Is there any way to retrieving everything from the sampling point without changing the domain classes? there is an example in the conference GraphQL vs Traditional Rest API, in the 25:41 min, but this example is in java, and we could not make the same using the graphQl .net.
The next example illustrates the type of query we want to make:
query GetAreas(){
areas(){
Id
samplingPoints{
Id
description
}
}
}
So the question is: Is there a way to this as in the video above, as we pass the samplingPoints, and resolve it, retrieving the samplingPoints for that area (in some query)?
Question resolved on github. For those trying to do the same, its really easy actually, we just have to had resolver inside the AreaType like this:
public class AreaType : ObjectGraphType<AreaRoot>
{
public AreaType(IRepository repository)
{
Name = "Area";
Field(x => x.Id, type: typeof(IdGraphType));
Field<ListGraphType<SamplingPointType>>("areaSamplingPoints",
resolve: context =>
{
return repository.GetAllByAreaId(context.Source?.Id);
});
}
}
notice the context.Source?.Id used to access the Area Id...
And also, if you are trying to access the arguments of the top level context, well, you can't, as stated here, but instead, you can access the variables passed to the query, not the best, but not the worst, so use: context.Variables.ValueFor("variableName")
I am getting the error only when I am using ProjectTo, I could not understand the underlying issue. (Automapper version am using 4.2.1.0)
"The specified type member 'Tags' is not supported in LINQ to Entities.
Only initializers, entity members, and entity navigation properties are supported."
We could do this manipulation in DTO as well, but I should stick to doing the manipulation in entity side only.
Do let me know the ways or work around I could handle this without upgrading the version. TIA
I want the computed value of Tags property of entity needs to be mapped to the Tags property of the DTO, but this works fine when I am doing the normal way.
Source/destination types
public class Template : IEntity<int>
{
public string Name { get; set; }
public int Id { get; set; }
public string _Tags { get; set; }
[NotMapped]
public List<string> Tags
{
get { return _Tags == null ? null : JsonConvert.DeserializeObject<List<string>>(_Tags); }
set { _Tags = JsonConvert.SerializeObject(value); }
}
}
Entity Config
internal sealed class TemplateConfig : EntityTypeConfiguration<Template>
{
public TemplateConfig()
{
Ignore(x => x.Tags);
HasKey(x => x.Id)
.Map(m =>
{
m.ToTable("Template");
m.Property(x => x.Id).HasColumnName("ID");
m.Property(x => x.Name).HasColumnName("Name");
m.Property(x => x._Tags).HasColumnName("Tags");
});
}
}
Destination DTO:
public class Template
{
public int Id { get; set; }
public string Name { get; set; }
public List<string> Tags { get; set; }
}
Mapping configuration
Mapper.CreateMap<Template, DTO.Template>();
Mapper.CreateMap<DTO.Template, Template>();
//These are just for information, but getting error only when using ProjectTo. (Ignore about the OData thing)
public virtual async Task<IQueryable<TDto>> Get(ODataQueryOptions<TDto> query)
{
try
{
var expands = query.GetExpandedPropertyNames();
//Assume the collection has the data from db
var test = Collection.ToList();
//getting the exception here
return await Collection.ProjectTo<TDto>(null, expands).AsTask();
}
catch(Exception ex)
{
throw ex;
}
}
//Controller method
public override async Task<IQueryable<Template>> Get(ODataQueryOptions<Template> query)
{
try
{
List<Template> result = (await base.Get(query)).ToList();
return result.AsEnumerable().AsQueryable();
}
catch(Exception ex)
{
throw ex;
}
}
This is happening because .ProjectTo<>(...) is building the select statement for you in SQL. As .Tags maps between your objects, it is being included in the select statement, and then entity framework is complaining that it can't do that (because of the NotMapped attribute).
Instead of using ProjectTo<>(...) you could just use a normal .ToList() and then use .Select(Mapper.Map<TDto>) or Mapper.Map<List<TDto>>(list).
That should work, as entity framework will populate your Tags field from the string field, and automapper can do the map ok.
public virtual async Task<IQueryable<TDto>> Get(ODataQueryOptions<TDto> query)
{
try
{
var expands = query.GetExpandedPropertyNames();
//Assume the collection has the data from db
var test = Collection.ToList();
var entities = expands.ToList();
// you can either use .Select to project using LINQ
var dtos = await entities.Select(Mapper.Map<TDto>).AsTask();
// or you can use Mapper.Map to a list of entities.
dtos = await Mapper.Map<List<TDto>>(entities).AsTask();
return dtos;
}
catch(Exception ex)
{
// side note, don't throw ex, you'll lose the stack trace
throw;
}
}
Have you tried implementing the ignore in the MappingConfiguration? I can't quite tell which direction you're having issues with, but something like:
Mapper.CreateMap<Template, DTO.Template>()
.ForMember(dest => dest.Tags, opts => opts.Ignore());
I have the following class:
public class Foo : IFoo
{
public object Id { get; set; }
public string someProperty { get; set;}
}
If you notice, the Id property it's an object type. This is very important because I don't wanna to have any MongoDb dependence on my Models, thus the same models will be used in others databases repositories.
So I have my FooMongoDBRepository where my class map are defined:
public class FooMongoDBRepository : IFooRepository{
public MongoDbSubscriberRepository (MongoDatabase database){
BsonSerializer.LookupSerializer(typeof(Foo));
if (!BsonClassMap.IsClassMapRegistered (typeof(Foo))) {
BsonClassMap.RegisterClassMap<Foo> (cm => {
cm.AutoMap ();
cm.SetIdMember (cm.GetMemberMap (c => c.Id));
});
}
}
}
This work fine for inserts, but when try to Upsert, my _id key is always NULL. Can someone help me, how to force the Id field to be generated without using annotations ?
Thanks in advanced!
Edit: Now It's working!, here is the code
cm.AutoMap ();
cm.GetMemberMap(c => c.Id).SetIgnoreIfDefault(true);
cm.SetIdMember (cm.GetMemberMap (c => c.Id));
cm.IdMemberMap.SetIdGenerator(ObjectIdGenerator.Instance);
You need to add
[BsonIgnoreIfDefault] to your Id
BsonClassMap.RegisterClassMap<MyClass>(cm => {
cm.AutoMap();
cm.GetMemberMap(c => c.SomeProperty)
.SetDefaultValue("abc")
.SetIgnoreIfDefault(true);
});
The sample above can be found here
i have searched the "whole" internetz for this question, and its a damn hard one to search for as its rather complicated. Try searching for "Fluent NHibernate Many to Many with a bridge table with extra columns" etc...
Okay, to make it easier to explain ill define some tables i can refer to.
Table: User, Table: Function, Table: User_Has_Function.
One User can have many Functions, and a Function can have many Users, this is linked in the bridge table User_Has_Function. The bridge table has extra columns which is only relevant to the relationship.
Well anyways iv found that FNH doesn't have any automatic solution to this, basically you have to use a one to many relation from User to User_Has_Function and many to one from User_Has_Function to Function, hence "[One] to [Many - Many] to [One]".
I have solved it like in this link http://sessionfactory.blogspot.com/2010/12/many-to-many-relationships-with.html just with FNH class mapping instead of xml obviously.
But im not satisfied with the solution, do i really have to do all this manually work to make this function properly? Also as it is now it inserts duplicates in the bridge table.
In my head i'm doing something wrong, cause i cant imagine there is no support for this. Just use SaveAndUpdate(), no duplicates are inserted and when i remove an entity the relation is removed as well, if no relations are left remove the entity itself etc.
Okay here are my entities and mappings, I am VERY new to Fluent NHibernate so don't yell to much if i have done something very wrong. :)
Entities:
public class XUser
{
public virtual int Id { get; set; }
...
public virtual IList<XUserHasXFunction> XUserHasXFunctions { get; set; }
public XUser()
{
XUserHasXFunctions = new List<XUserHasXFunction>();
}
public virtual void AddXFunction(XFunction xFunction, int isActive)
{
var xUserHasXFunction = new XUserHasXFunction()
{
XUser = this,
XFunction = xFunction,
DeployedDate = DateTime.Now
};
XUserHasXFunctions.Add(xUserHasXFunction);
xFunction.XUserHasXFunctions.Add(xUserHasXFunction);
}
public virtual void RemoveXFunction(XFunction xFunction)
{
var xUserHasXFunction = XUserHasXFunctions.Single(x => x.XFunction == xFunction);
XUserHasXFunctions.Remove(xUserHasXFunction);
xFunction.XUserHasXFunctions.Remove(xUserHasXFunction);
}
}
public class XFunction
{
public virtual int Id { get; set; }
...
public virtual IList<XUserHasXFunction> XUserHasXFunctions { get; set; }
public XFunction()
{
XUserHasXFunctions = new List<XUserHasXFunction>();
}
public virtual void AddXUser(XUser xUser, int isActive)
{
var xUserHasXFunction = new XUserHasXFunction()
{
XUser = xUser,
XFunction = this,
DeployedDate = DateTime.Now
};
XUserHasXFunctions.Add(xUserHasXFunction);
xUser.XUserHasXFunctions.Add(xUserHasXFunction);
}
public virtual void RemoveXUser(XUser xUser)
{
var xUserHasXFunction = XUserHasXFunctions.Single(x => x.XUser == xUser);
XUserHasXFunctions.Remove(xUserHasXFunction);
xUser.XUserHasXFunctions.Remove(xUserHasXFunction);
}
}
public class XUserHasXFunction
{
public virtual int Id { get; set; }
public virtual XUser XUser { get; set; }
public virtual XFunction XFunction { get; set; }
public virtual DateTime DeployedDate { get; set; }
}
Mappings:
public class XUserMap : ClassMap<XUser>
{
public XUserMap()
{
Id(x => x.Id, "ID").GeneratedBy.Sequence("SEQ").Column("ID");
Table("XUSER");
...
HasMany(x => x.XUserHasXFunctions).Cascade.All();
}
}
public class XFunctionMap : ClassMap<XFunction>
{
public XFunctionMap()
{
Id(x => x.Id, "ID").GeneratedBy.Sequence("SEQ").Column("ID");
Table("XFUNCTION");
...
HasMany(x => x.XUserHasXFunctions).Cascade.All();
}
}
public class XUserHasXFunctionMap : ClassMap<XUserHasXFunction>
{
public XUserHasXFunctionMap()
{
Id(x => x.Id, "ID").GeneratedBy.Sequence("SEQ").Column("ID");
Table("USER_HAS_FUNCTION");
Map(x => x.DeployedDate, "DEPLOYED_DATE");
References(x => x.XUser).ForeignKey("XUSER_ID").Cascade.SaveUpdate();
References(x => x.XFunction).ForeignKey("XFUNCTION_ID").Cascade.SaveUpdate();
}
}
I don't understand the "do i really have to do all this manual work" part. What "all this manual work"? There is nothing special there. The mapping is simple and the c# code doesn't have to do anything with persistency, it's plain old OO design.
If you get duplicated rows, there is something wrong with your mapping. It might be because of a inverse collection which had not been mapped as inverse.
If you don't need to navigate from Function to User, it's very easy. Either map the relation as entity, as described in the blog, or even easier, map it as a composite element.
(Sorry, I don't know Fluent)
<bag name="Functions" table="User_Has_Function">
<key column="UserId" />
<composite-element>
<many-to-one class="Function"/>
</composite-element>
</bag>
Edit:
From the comments:
The manual work I am talking about is the manual getting and checking
to remove and add relations from a user or function.
Are you talking about the required Add and Remove methods, which maintain the consistency of the relations? This is plain OO design. If you hadn't NHibernate, you would have to write it exactly the same (given the same class model).
delete a user from a function make it cascade all the way to user and
so forth...
No. Delete-cascading happens when an object is deleted. When you delete a user, you should cascade the user_has_function. From there, you may or may not cascade the functions. The same in the other direction. There is also the concept of "cascade-all-delete-orphans". It means that additionally to regular cascading, an object is deleted automatically when it is removed from the collection. This is not cascading. It is a kind of very basic garbage collection. If you want to make use of this in your case, you should not apply it to both the user->user_has_function collection and the function->user_has_function collection, because it would try to delete the object twice.
Don't forget to map both collections inverse. If you don't, you may get duplicated entries.
Make sure that the three mappings (the user->user_has_function collection, the function->user_has_function and the user_has_function class mapping) are using the same table name and foreign key names.
You don't need to mess around with composite keys.
I ended up doing something similar a while ago with user, group, user_group and ended up having to use a hacky method of having both objects exist on both sides and also manually choose between save or update.
I don't think there is a NICE way to do what you want, and I agree it is something that from a database point of view is fairly logical to do, but from a modelling point of view is a pain.
As I also assume you are having to use a composite key for your user_has_function table to make sure that you can have multiple functions for multiple users. Which I think most people try to avoid and end up using surrogate keys or some other approach.
I know this isn't an answer, but I never found a real answer to the same question when I posted it.
Here is a similar question I posted a while back:
Nhibernate composite key question
I ended up using an ISet instead of having the relations in ILists. ISet does not allow duplicates, but IList does. To use ISet you have to override the Equals and GetHashCode methods for the object stored in the ISet.
I cascade from XUser and XFunction and not the other way around, ended up that every record in all 3 tables were deleted when i deleted one entity because of cascading.
Here is how i solved it.
Entities:
public class XUser
{
public virtual int Id { get; set; }
...
public virtual ISet<XUserHasXFunction> XUserHasXFunctions { get; set; }
public XUser()
{
XUserHasXFunctions = new HashedSet<XUserHasXFunction>();
}
public virtual void AddXFunction(XFunction xFunction, int isActive)
{
var xUserHasXFunction = new XUserHasXFunction()
{
XUser = this,
XFunction = xFunction,
IsActive = isActive,
DeployedDate = DateTime.Now
};
if (XUserHasXFunctions.Contains(xUserHasXFunction) && xFunction.XUserHasXFunctions.Contains(xUserHasXFunction))
{
return;
}
XUserHasXFunctions.Add(xUserHasXFunction);
xFunction.XUserHasXFunctions.Add(xUserHasXFunction);
}
public virtual void RemoveXFunction(XFunction xFunction)
{
var xUserHasXFunction = XUserHasXFunctions.Single(x => x.XFunction == xFunction);
XUserHasXFunctions.Remove(xUserHasXFunction);
xFunction.XUserHasXFunctions.Remove(xUserHasXFunction);
}
}
public class XFunction
{
public virtual int Id { get; set; }
...
public virtual ISet<XUserHasXFunction> XUserHasXFunctions { get; set; }
public XFunction()
{
XUserHasXFunctions = new HashedSet<XUserHasXFunction>();
}
public virtual void AddXUser(XUser xUser, int isActive)
{
var xUserHasXFunction = new XUserHasXFunction()
{
XUser = xUser,
XFunction = this,
IsActive = isActive,
DeployedDate = DateTime.Now
};
if (XUserHasXFunctions.Contains(xUserHasXFunction) && xUser.XUserHasXFunctions.Contains(xUserHasXFunction))
{
return;
}
XUserHasXFunctions.Add(xUserHasXFunction);
xUser.XUserHasXFunctions.Add(xUserHasXFunction);
}
public virtual void RemoveXUser(XUser xUser)
{
var xUserHasXFunction = XUserHasXFunctions.Single(x => x.XUser == xUser);
XUserHasXFunctions.Remove(xUserHasXFunction);
xUser.XUserHasXFunctions.Remove(xUserHasXFunction);
}
}
public class XUserHasXFunction
{
public virtual int Id { get; set; }
...
public virtual DateTime DeployedDate { get; set; }
public override bool Equals(object obj)
{
if (obj == null)
return false;
var t = obj as XUserHasXFunction;
if (t == null)
return false;
return XUser == t.XUser && XFunction == t.XFunction;
}
public override int GetHashCode()
{
return (XUser.Id + "|" + XFunction.Id).GetHashCode();
}
}
Mappings:
public class XUserMap : ClassMap<XUser>
{
public XUserMap()
{
Id(x => x.Id, "ID").GeneratedBy.Sequence("SEQ").Column("ID");
Table("XUSER");
...
HasMany(x => x.XUserHasXFunctions).KeyColumn("XUSER_ID").Cascade.All();
}
}
public class XFunctionMap : ClassMap<XFunction>
{
public XFunctionMap()
{
Id(x => x.Id, "ID").GeneratedBy.Sequence("SEQ").Column("ID");
Table("XFUNCTION");
...
HasMany(x => x.XUserHasXFunctions)KeyColumn("XFUNCTION_ID").Cascade.All();
}
}
public class XUserHasXFunctionMap : ClassMap<XUserHasXFunction>
{
public XUserHasXFunctionMap()
{
Id(x => x.Id, "ID").GeneratedBy.Sequence("SEQ").Column("ID");
Table("XUSER_HAS_XFUNCTION");
...
Map(x => x.DeployedDate, "DEPLOYED_DATE");
References(x => x.XUser).Column("XUSER_ID");
References(x => x.XFunction).Column("XFUNCTION_ID");
}
}
Usage:
To add relations.
xFunction.AddXUser(xUser, isActive); //visa versa if you like to add a function to a user...
dao.Store(xFunction); //to actually add the relation in the db
now to remove relation
xFunction.RemoveXUser(xUser); //Realtion is removed but neither of the objects xFunction or xUser
dao.Store(xFunction); //...same
to remove a user and its relations.
dao.delete(xUser); //but the xFunction object it was connected to is not removed
//if you want the xFunction object to be removed you have to do that manually.
My web app uses half a dozen tables, each of which get populated when a user passes through the system. In order to do stats analysis I've written a database view to flatten these tables into a single view.
The view is working, however, I want to automate some tests around the view creation.
My idea to do this was to create a model/map and repository for the view - with list action only. My current implementation doesn't work.
This is my Repository:
namespace FunctionalTests.SpssView
{
public class SpssRepository
{
private readonly ISessionManager _sessionManager;
public SpssRepository(ISessionManager sessionManager)
{
_sessionManager = sessionManager;
}
public IList<Spss> ListFromSpssView()
{
ICriteria criteria = _sessionManager.GetSession().CreateCriteria(typeof(Spss));
return criteria.List<Spss>();
}
}
}
This is the model class:
namespace FunctionalTests.SpssView
{
public class Spss
{
public virtual String StudentId { get; set; }
public virtual String UPNSCN { get; set; }
...
}
}
And the mapping:
namespace FunctionalTests.SpssView
{
public sealed class SpssMap : ClassMap<Spss>
{
public SpssMap()
{
Id(x => x.StudentId).GeneratedBy.Assigned();
Map(x => x.UPNSCN);
...
}
}
}
I'm not entirely confident in the ID mapping - as it is just read from the view?
This is my test:
[Test]
public void ShouldPopulateAndRetrieveFromSpssView()
{
var mockSessionManager = new Mock<ISessionManager>();
mockSessionManager.Setup(x => x.GetSession()).Returns(_session);
var caseRepository = new CaseRepository(mockSessionManager.Object);
var caseList = caseRepository.ListCases();
Assert.That(caseList.Count, Is.EqualTo(2));
var repository = new SpssRepository(mockSessionManager.Object);
var spssList = repository.ListFromSpssView();
Assert.That(spssList.Count, Is.EqualTo(2));
}
Note the case list code - I put that in there to make sure the db connection was being made. This part of the test passes.
Running select * from spss; returns two results. (I'm using sql server 2005 fwiw)
And because this isn't production code, I created a new folder in my FunctionalTests visual studio project (I mention this, because it seems to me to be one of the main differences between this and my working repositories.) Should this make a difference??
Is it possible to test views like this?
Is there anyway I can see the sql that is being generated?
What am I doing wrong??!?
Thanks :)
Try adding:
public SpssMap()
{
Table("myViewBame"); // ADD THIS
Id(x => x.StudentId).GeneratedBy.Assigned();
Map(x => x.UPNSCN);
...
}
In order to see the generated SQL add this:
.ShowSql()
For example:
Fluently.Configure().Database(
MsSqlConfiguration.MsSql2005
.ConnectionString(
ConfigurationManager.ConnectionStrings["my"].ConnectionString).ShowSql())
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<MyClass>())
.BuildSessionFactory();