Thursday, 3 May 2012

Sharing common view model data in asp.net mvc with all the bells and whistles

In anything but the most trivial applications, there are common pieces of data you will want to share between your different views. Typical examples include the name of the signed-in user, pervasive summaries such as the last three items viewed, unread message counts or anything else that typically appears in a navigation element and is user-specific.

There are many techniques for doing this but they all fell short for us in way or another as we wanted to meet all the following requirements:

  • It should be strongly-typed (no using of viewbag/viewdata, thanks - in our opinion it makes it too difficult to refactor views later).
  • The views should be bound to the necessary models to enable intellisense.
  • It should support ctor dependency injection.
  • The common data should be available to controllers and views. This is especially useful when your application supports authentication and you need to show a property of the user in the view and need to use a property of the user in your controller action.
  • You should be able to opt-out if necessary.
  • Controller actions shouldn't need to change in any way - e.g. no calling of functions to populate the models.
The first step is to define a class that will represent this shared context. There isn't anything special about this class - it's a regular poco.

Here is an example that will store the current user and the number of unread messages in their inbox.
namespace Web.Models
{
    public class SharedContext
    {
        public User CurrentUser { get; set; }
        public int UnreadMessageCount { get;set; }
    }
}
Once you have created your shared context class you need to create a base view model. Again, it's just a poco but what is important is that it is able to hold an instance of the shared context which will be explained in more detail further below. Here is an example:
namespace Web.Models
{
    public class LayoutModel
    {
        public SharedContext Context { get; set; }
    }
}
This is the model you will bind to your _Layout file which takes care of the intellisense and "no loosely-typed view data" requirements. In your _Layout, if you would like to show the user's name, for example, you could access the property with @Model.Context.CurrentUser.Name (assuming you had a User class with a Name property, obviously).

The next step is to wire up these classes so they are populated automatically. We start by creating the interface for what I have called the view model factory.

An example of such an interface is as follows:
namespace Web.Mvc
{
    public interface IViewModelFactory
    {
        T Create<T>() where T : SharedContext, new();
        void Set<T>(T model) where T : SharedContext, new();
    }
}
The generic constraint ensures that we can access the context properties in the method implementations. Here is an example implementation of this interface:
namespace Web.Mvc
{
    public class ViewModelFactory : IViewModelFactory
    {
        private readonly IUserMessageService _userMessageService;
        private readonly IUserService _userService;

        public ViewModelFactory(IUserMessageService userMessageService,
            IUserService userService)
        {
            _userMessageService = userMessageService;
            _userService = userService;
        }

        public T Create<T>() where T : SharedContext, new()
        {
            var model = new T();
            Set(model);

            return model;
        }

        public void Set<T>(T model) where T : SharedContext, new()
        {
            var user = _userService.GetCurrent();

            model.User = user;
            model.UnreadMessageCount = _userMessageService.GetUnreadCount(user.Id);
        }
    }
}
Hopefully it's pretty straightforward. It's an implementation of the view model factory that is injected with several fictitious dependencies and generates a shared context. You will need to use your imagination here a bit.

At this point, you are going to want to register the view model factory in whatever DI container (I hope) you're using. In Unity, you might do something like:
container.RegisterType<IViewModelFactory, ViewModelFactory>(new PerCallContextLifeTimeManager());
Although usually not a fan of inheritance it works well for this scenario. You need a base class from which all your controllers will inherit (instead of from "Controller"). You might have done this already for various other reasons. Here is an example:
namespace Web.Mvc
{
    public class BaseController : Controller
    {
        public SharedContext Context { get; set; }
    }
}
In one of your action methods, you could access the current user via Context.CurrentUser.

We want our view model factory to be called automatically so our model is populated correctly. Here is the code for that attribute - you should be able to use this class as-is unless you've renamed the view model factory or shared context.
namespace Web.Mvc
{
    public class LayoutModelAttribute : ActionFilterAttribute
    {
        private readonly IViewModelFactory _viewModelFactory;
        
        public LayoutModelAttribute(IViewModelFactory viewModelFactory)
        {
            _viewModelFactory = viewModelFactory;
        }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var controller = filterContext.Controller as BaseController;
            if (controller != null)
            {
                (controller).Context = _viewModelFactory.Create<SharedContext>();
            }
        
            base.OnActionExecuting(filterContext);
        }

        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            viewModel = filterContext.Controller.ViewData.Model;
            var controller = filterContext.Controller as BaseController;

            var model = viewModel as LayoutModel;
            if (model != null)
            {
                (model).Context = controller != null && controller.Context != null
                    ? controller.Context
                    : _viewModelFactory.Create<SharedContext>();
            }

            base.OnResultExecuting(filterContext);
        }
    }
}
Taking a quick step back, this is what the attribute is doing:

We override OnActionExecuting and OnResultExecuting as these execute at different places within the asp.net mvc pipeline. To accomplish the requirement of being able to access the share context in a controller, the attribute needs to execute before the controller action; hence OnActionExecuting.

To intercept the model returned from the action and populate the required properties, we override OnResultExecuting which executes after the action has complete but before the view is rendered.

There are two different base-class checks here that allow us to opt-out of the shared context population. If the base class of your controller does not inherit from your new BaseController class, the view model factory will not be invoked before the action executes.

The other check is to ensure that the view model you are returning inherits from the new LayoutModel class. If not, the view model factory is bypassed. This means you can also use the shared context in your non-layout views which can be useful.

The next step is to register this attribute so it executes for every controller. There are different ways to do this, but I generally use the following as part of my site's bootstrapper (where container is our DI container):
GlobalFilters.Filters.Add(container.Resolve<LayoutModelAttribute>(), 1);
 

The last parameter (1 in this case) is there because I have an authentication filter higher up that should be checked before the new attribute is executed. You are likely to have different requirements in your own application.

Now that the infrastructure is complete, we can get on with building the application. Here is a sample view model that you might use on the homepage of your site:
namespace Web.Models
{
    public class HomeModel : LayoutModel
    {
        public string Content { get;set; }
    }
}
And here is the controller you might use:
namespace Web.Mvc
{
    public class HomeController : BaseController
    {
        public ActionResult Index()
        {
            return View(new HomeModel { Content = "Hello View Model Factory!" });
        }
    }
}

It might seem a bit complicated at first, but after several large applications this appears to provide the most maintainable and robust solution to this particular problem.

120 comments:

  1. Excellent article. This helped me out immensely.

    ReplyDelete
  2. Wow, this is exactly what the doctor ordered. One of the cleanest and least-fussy ways of dealing with shared context information between controllers and views. Thanks so much for the write-up!

    ReplyDelete
  3. What happens if you have a view making use of one of the Contextual properties, but an action that doesn't provide a model, or the model is null?

    ReplyDelete
    Replies
    1. That's a good point - at the moment, if you supply a model that is null or a model that doesn't inherit from LayoutModel, then none of the contextual properties will be populated. If you wanted this behaviour you would need to make sure any views you rendered from this action did not rely on any property within your shared model.

      Delete
  4. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
    2. I tried to include Ninject specific modifications to the code provided above, but this comment system didn't like the use of greater than and less than characters, probably thought they were HTML, rendering my comments useless.

      Delete
    3. Hello Rick, could you please send me the Ninject specific modification of this code to my email address? gattish@gmail.com
      Thanks in advance.

      Delete
  5. Where does PerCallContextLifeTimeManager come from?

    ReplyDelete
  6. This comment has been removed by the author.

    ReplyDelete
  7. Great post! Very helpful! How would I pass the controller's HttpContext to the SharedContext Model for creating some shared properties on the SharedContext Model based on that information? I'm using Autofac as my DI Container if that helps.

    Thanks!

    ReplyDelete
    Replies
    1. Hi Michael,

      What I've done in the past is to create something like an IHttpContextFactory and then an implementation that has a single method - something like:

      public interface IHttpContextFactory
      {
      HttpContextWrapper GetContext();
      }

      and then implement the interface like so:

      public class HttpContextFactory : IHttpContextFactory
      {
      public HttpContextWrapper GetContext()
      {
      return new HttpContextWrapper(HttpContext.Current);
      }
      }

      Now in autofac you can wire up the dependency IHttpContextFactory to the implementation HttpContextFactory and inject that into your ViewModelFactory.

      Inside your ViewModelFactory you can then call GetContext() and use whatever you want from the request/response to set properties on your SharedContext model.

      I hope this helps.

      Gav

      Delete
  8. Nice blog..Sharing common view model data in asp.net mvc with all the bells and whistles is very easy to understand..Keep on blogging..
    PHP training in chennai

    ReplyDelete
  9. I can feel this is the right way, while I still cannot to finish a sample by myself. Could you give me a whole sample solution. my mail: wangjij@gmail.com

    ReplyDelete
  10. Wonderful post. I like your post. Keep sharing.

    ppc training in chennai

    ReplyDelete
  11. This blog is having the general information. Got a creative work and this is very different one.We have to develop our creativity mind.This blog helps for this. Thank you for this blog. This is very interesting and useful.

    Mobile App Development Company in India

    ReplyDelete
  12. This looks like the right thing to solve some of my problem in my current MVC 5 project.

    I using Unity V4.0.1 and Unity.MVC V4.0.1 - both out of the box. All is well until this line of code:
    GlobalFilters.Filters.Add(container.Resolve(), 1);
    I've tryed adding it in the file UnityMVCActivator in the Start method just below the line DependencyResolver.SetResolver(...
    and in the file FilterConfig in the RegisterGlobalFilters method as
    var container = UnityConfig.GetConfiguredContainer(); filters.Add(container.Resolve());
    and some other places with the same result.
    I get squigly lines under Resolve.

    What an I doing wrong, Can someone please help me???

    Thanks

    /Morny

    ReplyDelete
  13. I have express a few of the articles on your website now, and I really like your style of blogging. I added it to my favorite’s blog site list and will be checking back soon… wordpress

    ReplyDelete
  14. Nice tutorial. Thanks for sharing the valuable information. it’s really helpful. Who want to learn this blog most helpful. Keep sharing on updated tutorials…
    Online DevOps Certification Course - Gangboard
    Best Devops Training institute in Chennai

    ReplyDelete
  15. Woah this blog is wonderful i like studying your posts. Keep up the great work! You understand, lots of persons are hunting around for this info, you could help them greatly.
    Data Science Training in Indira nagar
    Data Science Training in btm layout
    Python Training in Kalyan nagar
    Data Science training in Indira nagar
    Data Science Training in Marathahalli | Data Science training in Bangalore

    ReplyDelete
  16. Your good knowledge and kindness in playing with all the pieces were very useful. I don’t know what I would have done if I had not encountered such a step like this.

    angularjs-Training in pune

    angularjs Training in bangalore

    angularjs Training in bangalore

    angularjs Training in chennai

    automation anywhere online Training

    angularjs interview questions and answers

    ReplyDelete
  17. A universal message I suppose, not giving up is the formula for success I think. Some things take longer than others to accomplish, so people must understand that they should have their eyes on the goal, and that should keep them motivated to see it out til the end.
    python training in rajajinagar
    Python training in bangalore
    Python training in usa

    ReplyDelete
  18. Very nice post here and thanks for it .I always like and such a super contents of these post.
    Excellent and very cool idea and great content of different kinds of the valuable information's.

    Java training in Bangalore



    ReplyDelete
  19. Thank you for an additional great post. Exactly where else could anybody get that kind of facts in this kind of a ideal way of writing? I have a presentation next week, and I’m around the appear for this kind of data.
    Data science course in bangalore | Data Science training with placement in Bangalore

    ReplyDelete
  20. Superb. I really enjoyed very much with this article here. Really it is an amazing article I had ever read. I hope it will help a lot for all. Thank you so much for this amazing posts and please keep update like this excellent article.thank you for sharing such a great blog with us. expecting for your.
    SEO Training in Chennai
    JAVA Training in Chennai
    Big Data Training in Chennai
    Selenium Training in Chennai
    German Classes in chennai
    PHP Training in Chennai
    PHP Training in Anna Nagar

    ReplyDelete
  21. This is quite educational arrange. It has famous breeding about what I rarity to vouch. Colossal proverb. This trumpet is a famous tone to nab to troths. Congratulations on a career well achieved. This arrange is synchronous s informative impolites festivity to pity. I appreciated what you ok extremely here 
    Data Science Course in Indira nagar
    Data Science Course in btm layout
    Python course in Kalyan nagar
    Data Science course in Indira nagar
    Data Science Course in Marathahalli
    Data Science Course in BTM Layout
    Data science course in bangalore

    ReplyDelete
  22. I adore your websites way of raising the awareness on your readers. Data Centers

    ReplyDelete
  23. Thanks For sharing Your Information The Information shared Is Very Valuable Please Keep Updating Us Python Online Course Hadoop Online Course Data Science Online Course Aws Online Course

    ReplyDelete
  24. Thank you for allowing me to read it, welcome to the next in a recent article. And thanks for sharing the nice article, keep posting or updating news article.
    lg mobile service chennai
    lg mobile repair
    lg mobile service center near me
    lg mobile service center in velachery

    ReplyDelete
  25. Excellent artcle. I have tried same solution with AutoFac and MVC 5.2, it works for GET Action requests and populate common properties defined in SharedModel but during POST actions, SharedModel gets null assignment.

    ReplyDelete
  26. Nice Post! Thank you for sharing knowledge, it was very good post to update my knowledge and improve my skills. keep blogging.
    Java Training in Electronic City

    ReplyDelete
  27. hello..
    i really like your blog....
    very informative....also helps a lot...
    thank you so much for sharing...
    keep going on..
    https://www.exltech.in/dot-net-training.html

    ReplyDelete
  28. Good Post! Thank you so much for sharing this pretty post, it was so good to read and useful to improve my knowledge as updated one, keep blogging.
    Angular Js training in Electronic City

    ReplyDelete
  29. Superb such good information given keep sharing more thanks for it
    https://www.exltech.in/dot-net-training.html

    ReplyDelete
  30. For AWS training in Bangalore, Visit:
    AWS training in Bangalore

    ReplyDelete
  31. For AWS training in Bangalore, Visit:- AWS training in Bangalore

    ReplyDelete

  32. Thanks for sharing an informative post. https://bdmarket.blogspot.com/

    ReplyDelete
  33. 7 tips to start a career in digital marketing

    “Digital marketing is the marketing of product or service using digital technologies, mainly on the Internet, but also including mobile phones, display advertising, and any other digital medium”. This is the definition that you would get when you search for the term “Digital marketing” in google. Let’s give out a simpler explanation by saying, “the form of marketing, using the internet and technologies like phones, computer etc”.

    we have offered to the advanced syllabus course digital marketing for available join now

    more details click the link now

    [url]https://www.webdschool.com/digital-marketing-course-in-chennai.html[/url]

    ReplyDelete
  34. Web designing trends in 2020

    When we look into the trends, everything which is ruling today’s world was once a start up and slowly begun getting into. But Now they have literally transformed our lives on a tremendous note. To name a few, Facebook, Whats App, Twitter can be a promising proof for such a transformation and have a true impact on the digital world.

    we have offered to the advanced syllabus course web design and development for available join now

    more details click the link now

    [url]https://www.webdschool.com/web-development-course-in-chennai.html[/url]

    ReplyDelete
  35. Whatever we gathered information from the blogs, we should implement that in practically then only we can understand that sap bi tutorial for beginners exact thing clearly, but it’s no need to do it, because you have explained the concepts very well. It was crystal clear, keep sharing..

    ReplyDelete
  36. Whatever we gathered information from the blogs, we should implement that in practically then only we can understand that exact thing clearly, but it’s no need to do it, because you have explained the concepts very well. It was crystal clear, keep sharing.. learn data science

    ReplyDelete
  37. I am inspired with your post writing style & how continuously you describe this topic on hadoop online training. After reading your post, thanks for taking the time to discuss this, I feel happy about it and I love learning more about this topic.

    ReplyDelete
  38. Pretty article! I found some useful information in your blog, it was awesome to read, thanks for sharing this great content to my vision, keep sharing....

    sapui5 tutorial

    ReplyDelete
  39. Nice post. Thanks for sharing! I want people to know just how good this information is in your article.
    the article was very interesting.
    keep sharing your post

    most haunted place in world

    ReplyDelete
  40. I found some useful information in your blog, it was awesome to read, thanks for sharing this great content to my vision, keep sharing..
    AWS training in Chennai

    AWS Online Training in Chennai

    AWS training in Bangalore

    AWS training in Hyderabad

    AWS training in Coimbatore

    AWS training

    ReplyDelete
  41. useful and valuable details you shared, thanks for the important blog post. It helped me a lot.

    ReplyDelete
  42. Hi it's really nice and more informative blog,
    Thanks to share with us and keep more updates,

    https://www.porurtraining.in/data-science-training-in-porur-chennai
    https://www.porurtraining.in/android-training-in-porur-chennai
    https://www.porurtraining.in/devops-training-in-porur-chennai
    https://www.porurtraining.in/artificial-intelligence-training-in-porur-chennai

    ReplyDelete

  43. Thank you for your post. This is excellent information. It is amazing and wonderful to visit your site.Thanks for sharing such an informative blog. I have read your blog and I gathered some needful information from your post. Keep update your blog. Awaiting for your next update.

    Azure Training in Chennai

    Azure Training in Bangalore

    Azure Training in Hyderabad

    Azure Training in Pune

    Azure Training | microsoft azure certification | Azure Online Training Course

    Azure Online Training

    ReplyDelete
  44. Great post! I am actually getting ready to across this information, Linking is very useful thing. It's very helpful for this blog.Also great with all of the valuable information you have Keep up the good work you are doing well.
    DevOps Training in Chennai

    DevOps Online Training in Chennai

    DevOps Training in Bangalore

    DevOps Training in Hyderabad

    DevOps Training in Coimbatore

    DevOps Training

    DevOps Online Training

    ReplyDelete
  45. Good job! Fruitful article. I like this very much. It is very useful for my research. It shows your interest in this topic very well. I hope you will post some more information about the software. Please keep sharing!!
    Data Science Training In Chennai

    Data Science Online Training In Chennai

    Data Science Training In Bangalore

    Data Science Training In Hyderabad

    Data Science Training In Coimbatore

    Data Science Training

    Data Science Online Training

    ReplyDelete
  46. Pretty good post. I just stumbled upon your blog and wanted to say that I have really enjoyed reading your blog posts. Any way I’ll be subscribing to your feed and I hope you post again soon.
    IELTS Coaching in chennai

    German Classes in Chennai

    GRE Coaching Classes in Chennai

    TOEFL Coaching in Chennai

    spoken english classes in chennai | Communication training


    ReplyDelete
  47. This blog is having the general information. Got a creative work and this is very different one.We have to develop our creativity mind.This blog helps for this. Thank you for this blog. This is very interesting and useful.


    AWS Course in Chennai

    AWS Course in Bangalore

    AWS Course in Hyderabad

    AWS Course in Coimbatore

    AWS Course

    AWS Certification Course

    AWS Certification Training

    AWS Online Training

    AWS Training

    ReplyDelete
  48. Great post! I am actually getting ready to across this information, It's very helpful for this blog.Also great with all of the valuable information you have Keep up the good work you are doing well.
    Great post! I am actually getting ready to across this information, It's very helpful for this blog.Also great with all of the valuable information you have Keep up the good work you are doing well.

    DevOps Training in Chennai

    DevOps Online Training in Chennai

    DevOps Training in Bangalore

    DevOps Training in Hyderabad

    DevOps Training in Coimbatore

    DevOps Training

    DevOps Online Training

    ReplyDelete
  49. Beside internet platform, which is closely associated with this marketing approach, also includes instant mobile messaging, mobile apps, electronic billboards, and other channels. digital marketing training in hyderabad

    ReplyDelete
  50. This fundamental concept is what we have been forever referring to as data, and it is this data that only holds the key to literally everything in the world. data science course in hyderabad

    ReplyDelete
  51. Thanks for the Information.Interesting stuff to read.Great Article.
    Data Science Online Training

    ReplyDelete
  52. Try to establish a long term relationship with the Salesforce Integration Consultant and consider the effectiveness of their ongoing support before deciding to make them your future Salesforce consulting partner based on the future improvements. Salesforce training in Hyderabad

    ReplyDelete
  53. I’m happy I located this blog! From time to time, students want to cognitive the keys of productive literary essays composing. Your first-class knowledge about this good post can become a proper basis for such people. nice one
    data scientist course in hyderabad with placement

    ReplyDelete
  54. I read your article it is very interesting and every concept is very clear, thank you so much for sharing. AWS Certification Course in Chennai


    ReplyDelete
  55. หาคุณกำลังหาเกมส์ออนไลน์ที่สามารถสร้างรายได้ให้กับคุณ เรามีเกมส์แนะนำ เกมยิงปลา รูปแบบใหม่เล่นง่ายบนมือถือ คาสิโนออนไลน์ บนคอม เล่นได้ทุกอุปกรณ์รองรับทุกเครื่องมือ มีให้เลือกเล่นหลายเกมส์ เล่นได้ทั่วโลกเพราะนี้คือเกมส์ออนไลน์แบบใหม่ เกมยิงปลา

    ReplyDelete
  56. Probably the most serious football betting UFABET that's past description Find fun, excitement and excitement with slot games, hundred free recognition, quick withdrawal. If you would like to play slots for cash No need to have to put a great deal, without bare minimum, without need to have to share, waste moment because UFABET is in fact reduced, given heavily, a lot of good offers are actually waiting for you. Ready to guarantee pleasurable, regardless of whether it is Joker SlotXo fruit slot, we are able to call it an internet slot internet site for you especially. Able to play Including the assistance team which will facilitate slot formulas plus strategies of enjoying So you can be positive that any moment of pleasure and fun We will be there for someone to provide the potential customers of yours the best impression and additionally achievement.. บาคาร่า
    ufa
    ufabet
    แทงบอล
    แทงบอล
    แทงบอล

    ReplyDelete
  57. one fast silveOnline football betting Online casinos Baccarat on the internet In a handy manner, i99PRO 24 hour system can enable you to pleasantly bet on boxing. And as well boasts a full system Open just one single website, this can do everything. Since the heart desires actually There's additionally a Call Center personnel that are made twenty four several hours one day plus our website offers a wide range of solutions including web based football betting. Boxing betting online and casinos that are starting to be famous within this dimensions. And still well known as one of the top part. Our i99PRO website has also gained the confidence in addition to being total satisfaction of many gambling fanatics. And as well offers a wide range of football, boxing, basketball, lottery, web based casinos And there are a lot more different types of sports waiting for one to come in.r fox สล็อต

    ReplyDelete
  58. Why cash app won’t let me send money?
    Can’t send money on the cash app if you are facing the same issue as many other cash app users then you have come to the right place to know why Cash app won’t let me send money? at this place you will get the answer to this question, to know why cash app is stopping you from sending money you just have to click on this given link here.

    ReplyDelete
  59. This blog is having the overall data. Got an imaginative work and this is totally different one.We need to foster our inventiveness mind. This blog helps for this. Much thanks to you for this blog. This is exceptionally fascinating and helpful…

    AI Training in Hyderabad

    ReplyDelete
  60. Where does PerCallContextLifeTimeManager come from? shareit app vidmate 2017

    ReplyDelete
  61. Merkur Progress Gaming Desk Gold - Shootercasino
    Merkur Progress Gaming Desk Gold Razor Gold Merkur 1XBET Progress - 500gr. 메리트카지노 The Progress is the perfect balance for wet ボンズ カジノ shaves. Merkur has developed a $39.99 · ‎In stock

    ReplyDelete
  62. Nice blog and informative. I appreciate your efforts in this blog. Wishing you the best of luck in your future blogs.
    AI Patasala Data Science Courses

    ReplyDelete
  63. I read your article several times since you make so many excellent points. For the most part, your viewpoints are similar to mine. This is excellent material for your readers.digital marketing assignment help



    ReplyDelete

  64. Very Informative and useful... Keep it up the great work. I really appreciate your post.
    It shows like you spend more effort and time to write this blog

    https://bangaloredigitalmarketing.com/
    https://bangaloredigitalmarketing.com/digital-marketing-courses-in-bangalore/
    https://bangaloredigitalmarketing.com/seo-company-in-bangalore/
    https://bangaloredigitalmarketing.com/social-media-marketing-agency-in-bangalore/

    ReplyDelete
  65. Thank you for providing us with such a pleasant and informative blog. I've noticed that everyone says the same thing over and over. However, I was able to obtain some important and unique knowledge from your blog. International business assignment help

    ReplyDelete
  66. Bandicam Keygen is an awesome instrument causes you to enact the full premium highlights of Bandicam most recent form in zero of expense. Bandicam Registered Free

    ReplyDelete
  67. Users CoffeeCup Responsive Site Designer Serial Key with interactive effects and transitions. These techniques take your website Coffeecup Web Form Builder

    ReplyDelete
  68. Wish your family with a sweet Christmas message. Browse and share one of the fifty sweet Christmas wishes for family and express your most sincere feelings. Christmas Wishes For Love

    ReplyDelete

  69. https://designingcourses.in/graphic-designing-courses-in-bangalore/
    https://designingcourses.in/sap-mm-course-in-bangalore/
    https://designingcourses.in/sap-pp-training-in-bangalore/
    https://designingcourses.in/sap-sd-course-in-bangalore/
    https://designingcourses.in/sap-abap-course-in-bangalore/
    https://designingcourses.in/sap-course-in-bangalore/
    https://goo.gl/maps/vDVAMZPrWntMeRJVA

    Learn graphic designing courses in bangalore we provide best graphic designing training and 100% placement assistance

    ReplyDelete
  70. Excellent share! Thanks for explaining everything properly.

    Please check this amazing course below.

    Data Science Course in Vijayawada

    Discover Vidal International's best data science course in Vijayawada, designed for beginners and professionals. Enjoy flexible learning, 100% placement guarantee, and lifetime support.

    ReplyDelete
  71. I read your article several times since you make so many excellent points.
    waplus
    projectfreetv.onl

    ReplyDelete